game.cpp 135 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513
  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 <iomanip>
  18. #include <cmath>
  19. #include "client/renderingengine.h"
  20. #include "camera.h"
  21. #include "client.h"
  22. #include "client/clientevent.h"
  23. #include "client/gameui.h"
  24. #include "client/inputhandler.h"
  25. #include "client/texturepaths.h"
  26. #include "client/keys.h"
  27. #include "client/joystick_controller.h"
  28. #include "client/mapblock_mesh.h"
  29. #include "client/sound.h"
  30. #include "clientmap.h"
  31. #include "clientmedia.h" // For clientMediaUpdateCacheCopy
  32. #include "clouds.h"
  33. #include "config.h"
  34. #include "content_cao.h"
  35. #include "content/subgames.h"
  36. #include "client/event_manager.h"
  37. #include "fontengine.h"
  38. #include "gui/touchscreengui.h"
  39. #include "itemdef.h"
  40. #include "log.h"
  41. #include "filesys.h"
  42. #include "gameparams.h"
  43. #include "gettext.h"
  44. #include "gui/guiChatConsole.h"
  45. #include "gui/guiFormSpecMenu.h"
  46. #include "gui/guiKeyChangeMenu.h"
  47. #include "gui/guiPasswordChange.h"
  48. #include "gui/guiVolumeChange.h"
  49. #include "gui/mainmenumanager.h"
  50. #include "gui/profilergraph.h"
  51. #include "mapblock.h"
  52. #include "minimap.h"
  53. #include "nodedef.h" // Needed for determining pointing to nodes
  54. #include "nodemetadata.h"
  55. #include "particles.h"
  56. #include "porting.h"
  57. #include "profiler.h"
  58. #include "raycast.h"
  59. #include "server.h"
  60. #include "settings.h"
  61. #include "shader.h"
  62. #include "sky.h"
  63. #include "translation.h"
  64. #include "util/basic_macros.h"
  65. #include "util/directiontables.h"
  66. #include "util/pointedthing.h"
  67. #include "util/quicktune_shortcutter.h"
  68. #include "irrlicht_changes/static_text.h"
  69. #include "irr_ptr.h"
  70. #include "version.h"
  71. #include "script/scripting_client.h"
  72. #include "hud.h"
  73. #include "clientdynamicinfo.h"
  74. #include <IAnimatedMeshSceneNode.h>
  75. #if USE_SOUND
  76. #include "client/sound/sound_openal.h"
  77. #endif
  78. /*
  79. Text input system
  80. */
  81. struct TextDestNodeMetadata : public TextDest
  82. {
  83. TextDestNodeMetadata(v3s16 p, Client *client)
  84. {
  85. m_p = p;
  86. m_client = client;
  87. }
  88. // This is deprecated I guess? -celeron55
  89. void gotText(const std::wstring &text)
  90. {
  91. std::string ntext = wide_to_utf8(text);
  92. infostream << "Submitting 'text' field of node at (" << m_p.X << ","
  93. << m_p.Y << "," << m_p.Z << "): " << ntext << std::endl;
  94. StringMap fields;
  95. fields["text"] = ntext;
  96. m_client->sendNodemetaFields(m_p, "", fields);
  97. }
  98. void gotText(const StringMap &fields)
  99. {
  100. m_client->sendNodemetaFields(m_p, "", fields);
  101. }
  102. v3s16 m_p;
  103. Client *m_client;
  104. };
  105. struct TextDestPlayerInventory : public TextDest
  106. {
  107. TextDestPlayerInventory(Client *client)
  108. {
  109. m_client = client;
  110. m_formname.clear();
  111. }
  112. TextDestPlayerInventory(Client *client, const std::string &formname)
  113. {
  114. m_client = client;
  115. m_formname = formname;
  116. }
  117. void gotText(const StringMap &fields)
  118. {
  119. m_client->sendInventoryFields(m_formname, fields);
  120. }
  121. Client *m_client;
  122. };
  123. struct LocalFormspecHandler : public TextDest
  124. {
  125. LocalFormspecHandler(const std::string &formname)
  126. {
  127. m_formname = formname;
  128. }
  129. LocalFormspecHandler(const std::string &formname, Client *client):
  130. m_client(client)
  131. {
  132. m_formname = formname;
  133. }
  134. void gotText(const StringMap &fields)
  135. {
  136. if (m_formname == "MT_PAUSE_MENU") {
  137. if (fields.find("btn_sound") != fields.end()) {
  138. g_gamecallback->changeVolume();
  139. return;
  140. }
  141. if (fields.find("btn_key_config") != fields.end()) {
  142. g_gamecallback->keyConfig();
  143. return;
  144. }
  145. if (fields.find("btn_exit_menu") != fields.end()) {
  146. g_gamecallback->disconnect();
  147. return;
  148. }
  149. if (fields.find("btn_exit_os") != fields.end()) {
  150. g_gamecallback->exitToOS();
  151. #ifndef __ANDROID__
  152. RenderingEngine::get_raw_device()->closeDevice();
  153. #endif
  154. return;
  155. }
  156. if (fields.find("btn_change_password") != fields.end()) {
  157. g_gamecallback->changePassword();
  158. return;
  159. }
  160. return;
  161. }
  162. if (m_formname == "MT_DEATH_SCREEN") {
  163. assert(m_client != nullptr);
  164. if (fields.find("quit") != fields.end())
  165. m_client->sendRespawn();
  166. return;
  167. }
  168. if (m_client->modsLoaded())
  169. m_client->getScript()->on_formspec_input(m_formname, fields);
  170. }
  171. Client *m_client = nullptr;
  172. };
  173. /* Form update callback */
  174. class NodeMetadataFormSource: public IFormSource
  175. {
  176. public:
  177. NodeMetadataFormSource(ClientMap *map, v3s16 p):
  178. m_map(map),
  179. m_p(p)
  180. {
  181. }
  182. const std::string &getForm() const
  183. {
  184. static const std::string empty_string = "";
  185. NodeMetadata *meta = m_map->getNodeMetadata(m_p);
  186. if (!meta)
  187. return empty_string;
  188. return meta->getString("formspec");
  189. }
  190. virtual std::string resolveText(const std::string &str)
  191. {
  192. NodeMetadata *meta = m_map->getNodeMetadata(m_p);
  193. if (!meta)
  194. return str;
  195. return meta->resolveString(str);
  196. }
  197. ClientMap *m_map;
  198. v3s16 m_p;
  199. };
  200. class PlayerInventoryFormSource: public IFormSource
  201. {
  202. public:
  203. PlayerInventoryFormSource(Client *client):
  204. m_client(client)
  205. {
  206. }
  207. const std::string &getForm() const
  208. {
  209. LocalPlayer *player = m_client->getEnv().getLocalPlayer();
  210. return player->inventory_formspec;
  211. }
  212. Client *m_client;
  213. };
  214. class NodeDugEvent : public MtEvent
  215. {
  216. public:
  217. v3s16 p;
  218. MapNode n;
  219. NodeDugEvent(v3s16 p, MapNode n):
  220. p(p),
  221. n(n)
  222. {}
  223. Type getType() const { return NODE_DUG; }
  224. };
  225. class SoundMaker
  226. {
  227. ISoundManager *m_sound;
  228. const NodeDefManager *m_ndef;
  229. public:
  230. bool makes_footstep_sound = true;
  231. float m_player_step_timer = 0.0f;
  232. float m_player_jump_timer = 0.0f;
  233. SoundSpec m_player_step_sound;
  234. SoundSpec m_player_leftpunch_sound;
  235. // Second sound made on left punch, currently used for item 'use' sound
  236. SoundSpec m_player_leftpunch_sound2;
  237. SoundSpec m_player_rightpunch_sound;
  238. SoundMaker(ISoundManager *sound, const NodeDefManager *ndef) :
  239. m_sound(sound), m_ndef(ndef) {}
  240. void playPlayerStep()
  241. {
  242. if (m_player_step_timer <= 0 && m_player_step_sound.exists()) {
  243. m_player_step_timer = 0.03;
  244. if (makes_footstep_sound)
  245. m_sound->playSound(0, m_player_step_sound);
  246. }
  247. }
  248. void playPlayerJump()
  249. {
  250. if (m_player_jump_timer <= 0.0f) {
  251. m_player_jump_timer = 0.2f;
  252. m_sound->playSound(0, SoundSpec("player_jump", 0.5f));
  253. }
  254. }
  255. static void viewBobbingStep(MtEvent *e, void *data)
  256. {
  257. SoundMaker *sm = (SoundMaker *)data;
  258. sm->playPlayerStep();
  259. }
  260. static void playerRegainGround(MtEvent *e, void *data)
  261. {
  262. SoundMaker *sm = (SoundMaker *)data;
  263. sm->playPlayerStep();
  264. }
  265. static void playerJump(MtEvent *e, void *data)
  266. {
  267. SoundMaker *sm = (SoundMaker *)data;
  268. sm->playPlayerJump();
  269. }
  270. static void cameraPunchLeft(MtEvent *e, void *data)
  271. {
  272. SoundMaker *sm = (SoundMaker *)data;
  273. sm->m_sound->playSound(0, sm->m_player_leftpunch_sound);
  274. sm->m_sound->playSound(0, sm->m_player_leftpunch_sound2);
  275. }
  276. static void cameraPunchRight(MtEvent *e, void *data)
  277. {
  278. SoundMaker *sm = (SoundMaker *)data;
  279. sm->m_sound->playSound(0, sm->m_player_rightpunch_sound);
  280. }
  281. static void nodeDug(MtEvent *e, void *data)
  282. {
  283. SoundMaker *sm = (SoundMaker *)data;
  284. NodeDugEvent *nde = (NodeDugEvent *)e;
  285. sm->m_sound->playSound(0, sm->m_ndef->get(nde->n).sound_dug);
  286. }
  287. static void playerDamage(MtEvent *e, void *data)
  288. {
  289. SoundMaker *sm = (SoundMaker *)data;
  290. sm->m_sound->playSound(0, SoundSpec("player_damage", 0.5));
  291. }
  292. static void playerFallingDamage(MtEvent *e, void *data)
  293. {
  294. SoundMaker *sm = (SoundMaker *)data;
  295. sm->m_sound->playSound(0, SoundSpec("player_falling_damage", 0.5));
  296. }
  297. void registerReceiver(MtEventManager *mgr)
  298. {
  299. mgr->reg(MtEvent::VIEW_BOBBING_STEP, SoundMaker::viewBobbingStep, this);
  300. mgr->reg(MtEvent::PLAYER_REGAIN_GROUND, SoundMaker::playerRegainGround, this);
  301. mgr->reg(MtEvent::PLAYER_JUMP, SoundMaker::playerJump, this);
  302. mgr->reg(MtEvent::CAMERA_PUNCH_LEFT, SoundMaker::cameraPunchLeft, this);
  303. mgr->reg(MtEvent::CAMERA_PUNCH_RIGHT, SoundMaker::cameraPunchRight, this);
  304. mgr->reg(MtEvent::NODE_DUG, SoundMaker::nodeDug, this);
  305. mgr->reg(MtEvent::PLAYER_DAMAGE, SoundMaker::playerDamage, this);
  306. mgr->reg(MtEvent::PLAYER_FALLING_DAMAGE, SoundMaker::playerFallingDamage, this);
  307. }
  308. void step(float dtime)
  309. {
  310. m_player_step_timer -= dtime;
  311. m_player_jump_timer -= dtime;
  312. }
  313. };
  314. typedef s32 SamplerLayer_t;
  315. class GameGlobalShaderConstantSetter : public IShaderConstantSetter
  316. {
  317. Sky *m_sky;
  318. Client *m_client;
  319. CachedVertexShaderSetting<float> m_animation_timer_vertex{"animationTimer"};
  320. CachedPixelShaderSetting<float> m_animation_timer_pixel{"animationTimer"};
  321. CachedVertexShaderSetting<float>
  322. m_animation_timer_delta_vertex{"animationTimerDelta"};
  323. CachedPixelShaderSetting<float>
  324. m_animation_timer_delta_pixel{"animationTimerDelta"};
  325. CachedPixelShaderSetting<float, 3> m_day_light{"dayLight"};
  326. CachedPixelShaderSetting<float, 3> m_eye_position_pixel{"eyePosition"};
  327. CachedVertexShaderSetting<float, 3> m_eye_position_vertex{"eyePosition"};
  328. CachedPixelShaderSetting<float, 3> m_minimap_yaw{"yawVec"};
  329. CachedPixelShaderSetting<float, 3> m_camera_offset_pixel{"cameraOffset"};
  330. CachedPixelShaderSetting<float, 3> m_camera_offset_vertex{"cameraOffset"};
  331. CachedPixelShaderSetting<SamplerLayer_t> m_texture0{"texture0"};
  332. CachedPixelShaderSetting<SamplerLayer_t> m_texture1{"texture1"};
  333. CachedPixelShaderSetting<SamplerLayer_t> m_texture2{"texture2"};
  334. CachedPixelShaderSetting<SamplerLayer_t> m_texture3{"texture3"};
  335. CachedVertexShaderSetting<float, 2> m_texel_size0_vertex{"texelSize0"};
  336. CachedPixelShaderSetting<float, 2> m_texel_size0_pixel{"texelSize0"};
  337. v2f m_texel_size0;
  338. CachedStructPixelShaderSetting<float, 7> m_exposure_params_pixel{
  339. "exposureParams",
  340. std::array<const char*, 7> {
  341. "luminanceMin", "luminanceMax", "exposureCorrection",
  342. "speedDarkBright", "speedBrightDark", "centerWeightPower",
  343. "compensationFactor"
  344. }};
  345. float m_user_exposure_compensation;
  346. bool m_bloom_enabled;
  347. CachedPixelShaderSetting<float> m_bloom_intensity_pixel{"bloomIntensity"};
  348. float m_bloom_intensity;
  349. CachedPixelShaderSetting<float> m_bloom_strength_pixel{"bloomStrength"};
  350. float m_bloom_strength;
  351. CachedPixelShaderSetting<float> m_bloom_radius_pixel{"bloomRadius"};
  352. float m_bloom_radius;
  353. CachedPixelShaderSetting<float> m_saturation_pixel{"saturation"};
  354. bool m_volumetric_light_enabled;
  355. CachedPixelShaderSetting<float, 3>
  356. m_sun_position_pixel{"sunPositionScreen"};
  357. CachedPixelShaderSetting<float> m_sun_brightness_pixel{"sunBrightness"};
  358. CachedPixelShaderSetting<float, 3>
  359. m_moon_position_pixel{"moonPositionScreen"};
  360. CachedPixelShaderSetting<float> m_moon_brightness_pixel{"moonBrightness"};
  361. CachedPixelShaderSetting<float>
  362. m_volumetric_light_strength_pixel{"volumetricLightStrength"};
  363. static constexpr std::array<const char*, 4> SETTING_CALLBACKS = {
  364. "exposure_compensation",
  365. "bloom_intensity",
  366. "bloom_strength_factor",
  367. "bloom_radius"
  368. };
  369. public:
  370. void onSettingsChange(const std::string &name)
  371. {
  372. if (name == "exposure_compensation")
  373. m_user_exposure_compensation = g_settings->getFloat("exposure_compensation", -1.0f, 1.0f);
  374. if (name == "bloom_intensity")
  375. m_bloom_intensity = g_settings->getFloat("bloom_intensity", 0.01f, 1.0f);
  376. if (name == "bloom_strength_factor")
  377. m_bloom_strength = RenderingEngine::BASE_BLOOM_STRENGTH * g_settings->getFloat("bloom_strength_factor", 0.1f, 10.0f);
  378. if (name == "bloom_radius")
  379. m_bloom_radius = g_settings->getFloat("bloom_radius", 0.1f, 8.0f);
  380. }
  381. static void settingsCallback(const std::string &name, void *userdata)
  382. {
  383. reinterpret_cast<GameGlobalShaderConstantSetter*>(userdata)->onSettingsChange(name);
  384. }
  385. void setSky(Sky *sky) { m_sky = sky; }
  386. GameGlobalShaderConstantSetter(Sky *sky, Client *client) :
  387. m_sky(sky),
  388. m_client(client)
  389. {
  390. for (auto &name : SETTING_CALLBACKS)
  391. g_settings->registerChangedCallback(name, settingsCallback, this);
  392. m_user_exposure_compensation = g_settings->getFloat("exposure_compensation", -1.0f, 1.0f);
  393. m_bloom_enabled = g_settings->getBool("enable_bloom");
  394. m_bloom_intensity = g_settings->getFloat("bloom_intensity", 0.01f, 1.0f);
  395. m_bloom_strength = RenderingEngine::BASE_BLOOM_STRENGTH * g_settings->getFloat("bloom_strength_factor", 0.1f, 10.0f);
  396. m_bloom_radius = g_settings->getFloat("bloom_radius", 0.1f, 8.0f);
  397. m_volumetric_light_enabled = g_settings->getBool("enable_volumetric_lighting") && m_bloom_enabled;
  398. }
  399. ~GameGlobalShaderConstantSetter()
  400. {
  401. for (auto &name : SETTING_CALLBACKS)
  402. g_settings->deregisterChangedCallback(name, settingsCallback, this);
  403. }
  404. void onSetConstants(video::IMaterialRendererServices *services) override
  405. {
  406. u32 daynight_ratio = (float)m_client->getEnv().getDayNightRatio();
  407. video::SColorf sunlight;
  408. get_sunlight_color(&sunlight, daynight_ratio);
  409. m_day_light.set(sunlight, services);
  410. u32 animation_timer = m_client->getEnv().getFrameTime() % 1000000;
  411. float animation_timer_f = (float)animation_timer / 100000.f;
  412. m_animation_timer_vertex.set(&animation_timer_f, services);
  413. m_animation_timer_pixel.set(&animation_timer_f, services);
  414. float animation_timer_delta_f = (float)m_client->getEnv().getFrameTimeDelta() / 100000.f;
  415. m_animation_timer_delta_vertex.set(&animation_timer_delta_f, services);
  416. m_animation_timer_delta_pixel.set(&animation_timer_delta_f, services);
  417. v3f epos = m_client->getEnv().getLocalPlayer()->getEyePosition();
  418. m_eye_position_pixel.set(epos, services);
  419. m_eye_position_vertex.set(epos, services);
  420. if (m_client->getMinimap()) {
  421. v3f minimap_yaw = m_client->getMinimap()->getYawVec();
  422. m_minimap_yaw.set(minimap_yaw, services);
  423. }
  424. v3f offset = intToFloat(m_client->getCamera()->getOffset(), BS);
  425. m_camera_offset_pixel.set(offset, services);
  426. m_camera_offset_vertex.set(offset, services);
  427. SamplerLayer_t tex_id;
  428. tex_id = 0;
  429. m_texture0.set(&tex_id, services);
  430. tex_id = 1;
  431. m_texture1.set(&tex_id, services);
  432. tex_id = 2;
  433. m_texture2.set(&tex_id, services);
  434. tex_id = 3;
  435. m_texture3.set(&tex_id, services);
  436. m_texel_size0_vertex.set(m_texel_size0, services);
  437. m_texel_size0_pixel.set(m_texel_size0, services);
  438. const AutoExposure &exposure_params = m_client->getEnv().getLocalPlayer()->getLighting().exposure;
  439. std::array<float, 7> exposure_buffer = {
  440. std::pow(2.0f, exposure_params.luminance_min),
  441. std::pow(2.0f, exposure_params.luminance_max),
  442. exposure_params.exposure_correction,
  443. exposure_params.speed_dark_bright,
  444. exposure_params.speed_bright_dark,
  445. exposure_params.center_weight_power,
  446. powf(2.f, m_user_exposure_compensation)
  447. };
  448. m_exposure_params_pixel.set(exposure_buffer.data(), services);
  449. if (m_bloom_enabled) {
  450. m_bloom_intensity_pixel.set(&m_bloom_intensity, services);
  451. m_bloom_radius_pixel.set(&m_bloom_radius, services);
  452. m_bloom_strength_pixel.set(&m_bloom_strength, services);
  453. }
  454. const auto &lighting = m_client->getEnv().getLocalPlayer()->getLighting();
  455. float saturation = lighting.saturation;
  456. m_saturation_pixel.set(&saturation, services);
  457. if (m_volumetric_light_enabled) {
  458. // Map directional light to screen space
  459. auto camera_node = m_client->getCamera()->getCameraNode();
  460. core::matrix4 transform = camera_node->getProjectionMatrix();
  461. transform *= camera_node->getViewMatrix();
  462. if (m_sky->getSunVisible()) {
  463. v3f sun_position = camera_node->getAbsolutePosition() +
  464. 10000.f * m_sky->getSunDirection();
  465. transform.transformVect(sun_position);
  466. sun_position.normalize();
  467. m_sun_position_pixel.set(sun_position, services);
  468. float sun_brightness = core::clamp(107.143f * m_sky->getSunDirection().Y, 0.f, 1.f);
  469. m_sun_brightness_pixel.set(&sun_brightness, services);
  470. } else {
  471. m_sun_position_pixel.set(v3f(0.f, 0.f, -1.f), services);
  472. float sun_brightness = 0.f;
  473. m_sun_brightness_pixel.set(&sun_brightness, services);
  474. }
  475. if (m_sky->getMoonVisible()) {
  476. v3f moon_position = camera_node->getAbsolutePosition() +
  477. 10000.f * m_sky->getMoonDirection();
  478. transform.transformVect(moon_position);
  479. moon_position.normalize();
  480. m_moon_position_pixel.set(moon_position, services);
  481. float moon_brightness = core::clamp(107.143f * m_sky->getMoonDirection().Y, 0.f, 1.f);
  482. m_moon_brightness_pixel.set(&moon_brightness, services);
  483. } else {
  484. m_moon_position_pixel.set(v3f(0.f, 0.f, -1.f), services);
  485. float moon_brightness = 0.f;
  486. m_moon_brightness_pixel.set(&moon_brightness, services);
  487. }
  488. float volumetric_light_strength = lighting.volumetric_light_strength;
  489. m_volumetric_light_strength_pixel.set(&volumetric_light_strength, services);
  490. }
  491. }
  492. void onSetMaterial(const video::SMaterial &material) override
  493. {
  494. video::ITexture *texture = material.getTexture(0);
  495. if (texture) {
  496. core::dimension2du size = texture->getSize();
  497. m_texel_size0 = v2f(1.f / size.Width, 1.f / size.Height);
  498. } else {
  499. m_texel_size0 = v2f();
  500. }
  501. }
  502. };
  503. class GameGlobalShaderConstantSetterFactory : public IShaderConstantSetterFactory
  504. {
  505. Sky *m_sky = nullptr;
  506. Client *m_client;
  507. std::vector<GameGlobalShaderConstantSetter *> created_nosky;
  508. public:
  509. GameGlobalShaderConstantSetterFactory(Client *client) :
  510. m_client(client)
  511. {}
  512. void setSky(Sky *sky) {
  513. m_sky = sky;
  514. for (GameGlobalShaderConstantSetter *ggscs : created_nosky) {
  515. ggscs->setSky(m_sky);
  516. }
  517. created_nosky.clear();
  518. }
  519. virtual IShaderConstantSetter* create()
  520. {
  521. auto *scs = new GameGlobalShaderConstantSetter(m_sky, m_client);
  522. if (!m_sky)
  523. created_nosky.push_back(scs);
  524. return scs;
  525. }
  526. };
  527. #define SIZE_TAG "size[11,5.5,true]" // Fixed size (ignored in touchscreen mode)
  528. /****************************************************************************
  529. ****************************************************************************/
  530. const static float object_hit_delay = 0.2;
  531. /* The reason the following structs are not anonymous structs within the
  532. * class is that they are not used by the majority of member functions and
  533. * many functions that do require objects of thse types do not modify them
  534. * (so they can be passed as a const qualified parameter)
  535. */
  536. struct GameRunData {
  537. u16 dig_index;
  538. u16 new_playeritem;
  539. PointedThing pointed_old;
  540. bool digging;
  541. bool punching;
  542. bool btn_down_for_dig;
  543. bool dig_instantly;
  544. bool digging_blocked;
  545. bool reset_jump_timer;
  546. float nodig_delay_timer;
  547. float dig_time;
  548. float dig_time_complete;
  549. float repeat_place_timer;
  550. float object_hit_delay_timer;
  551. float time_from_last_punch;
  552. ClientActiveObject *selected_object;
  553. float jump_timer_up; // from key up until key down
  554. float jump_timer_down; // since last key down
  555. float jump_timer_down_before; // from key down until key down again
  556. float damage_flash;
  557. float update_draw_list_timer;
  558. float touch_blocks_timer;
  559. f32 fog_range;
  560. v3f update_draw_list_last_cam_dir;
  561. float time_of_day_smooth;
  562. };
  563. class Game;
  564. struct ClientEventHandler
  565. {
  566. void (Game::*handler)(ClientEvent *, CameraOrientation *);
  567. };
  568. /****************************************************************************
  569. THE GAME
  570. ****************************************************************************/
  571. using PausedNodesList = std::vector<std::pair<irr_ptr<scene::IAnimatedMeshSceneNode>, float>>;
  572. /* This is not intended to be a public class. If a public class becomes
  573. * desirable then it may be better to create another 'wrapper' class that
  574. * hides most of the stuff in this class (nothing in this class is required
  575. * by any other file) but exposes the public methods/data only.
  576. */
  577. class Game {
  578. public:
  579. Game();
  580. ~Game();
  581. bool startup(bool *kill,
  582. InputHandler *input,
  583. RenderingEngine *rendering_engine,
  584. const GameStartData &game_params,
  585. std::string &error_message,
  586. bool *reconnect,
  587. ChatBackend *chat_backend);
  588. void run();
  589. void shutdown();
  590. protected:
  591. // Basic initialisation
  592. bool init(const std::string &map_dir, const std::string &address,
  593. u16 port, const SubgameSpec &gamespec);
  594. bool initSound();
  595. bool createSingleplayerServer(const std::string &map_dir,
  596. const SubgameSpec &gamespec, u16 port);
  597. void copyServerClientCache();
  598. // Client creation
  599. bool createClient(const GameStartData &start_data);
  600. bool initGui();
  601. // Client connection
  602. bool connectToServer(const GameStartData &start_data,
  603. bool *connect_ok, bool *aborted);
  604. bool getServerContent(bool *aborted);
  605. // Main loop
  606. void updateInteractTimers(f32 dtime);
  607. bool checkConnection();
  608. bool handleCallbacks();
  609. void processQueues();
  610. void updateProfilers(const RunStats &stats, const FpsControl &draw_times, f32 dtime);
  611. void updateDebugState();
  612. void updateStats(RunStats *stats, const FpsControl &draw_times, f32 dtime);
  613. void updateProfilerGraphs(ProfilerGraph *graph);
  614. // Input related
  615. void processUserInput(f32 dtime);
  616. void processKeyInput();
  617. void processItemSelection(u16 *new_playeritem);
  618. void dropSelectedItem(bool single_item = false);
  619. void openInventory();
  620. void openConsole(float scale, const wchar_t *line=NULL);
  621. void toggleFreeMove();
  622. void toggleFreeMoveAlt();
  623. void togglePitchMove();
  624. void toggleFast();
  625. void toggleNoClip();
  626. void toggleCinematic();
  627. void toggleBlockBounds();
  628. void toggleAutoforward();
  629. void toggleMinimap(bool shift_pressed);
  630. void toggleFog();
  631. void toggleDebug();
  632. void toggleUpdateCamera();
  633. void increaseViewRange();
  634. void decreaseViewRange();
  635. void toggleFullViewRange();
  636. void checkZoomEnabled();
  637. void updateCameraDirection(CameraOrientation *cam, float dtime);
  638. void updateCameraOrientation(CameraOrientation *cam, float dtime);
  639. void updatePlayerControl(const CameraOrientation &cam);
  640. void updatePauseState();
  641. void step(f32 dtime);
  642. void processClientEvents(CameraOrientation *cam);
  643. void updateCamera(f32 dtime);
  644. void updateSound(f32 dtime);
  645. void processPlayerInteraction(f32 dtime, bool show_hud);
  646. /*!
  647. * Returns the object or node the player is pointing at.
  648. * Also updates the selected thing in the Hud.
  649. *
  650. * @param[in] shootline the shootline, starting from
  651. * the camera position. This also gives the maximal distance
  652. * of the search.
  653. * @param[in] liquids_pointable if false, liquids are ignored
  654. * @param[in] pointabilities item specific pointable overriding
  655. * @param[in] look_for_object if false, objects are ignored
  656. * @param[in] camera_offset offset of the camera
  657. * @param[out] selected_object the selected object or
  658. * NULL if not found
  659. */
  660. PointedThing updatePointedThing(
  661. const core::line3d<f32> &shootline, bool liquids_pointable,
  662. const std::optional<Pointabilities> &pointabilities,
  663. bool look_for_object, const v3s16 &camera_offset);
  664. void handlePointingAtNothing(const ItemStack &playerItem);
  665. void handlePointingAtNode(const PointedThing &pointed,
  666. const ItemStack &selected_item, const ItemStack &hand_item, f32 dtime);
  667. void handlePointingAtObject(const PointedThing &pointed, const ItemStack &playeritem,
  668. const v3f &player_position, bool show_debug);
  669. void handleDigging(const PointedThing &pointed, const v3s16 &nodepos,
  670. const ItemStack &selected_item, const ItemStack &hand_item, f32 dtime);
  671. void updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime,
  672. const CameraOrientation &cam);
  673. void updateClouds(float dtime);
  674. void updateShadows();
  675. void drawScene(ProfilerGraph *graph, RunStats *stats);
  676. // Misc
  677. void showOverlayMessage(const char *msg, float dtime, int percent,
  678. bool draw_clouds = true);
  679. static void settingChangedCallback(const std::string &setting_name, void *data);
  680. void readSettings();
  681. inline bool isKeyDown(GameKeyType k)
  682. {
  683. return input->isKeyDown(k);
  684. }
  685. inline bool wasKeyDown(GameKeyType k)
  686. {
  687. return input->wasKeyDown(k);
  688. }
  689. inline bool wasKeyPressed(GameKeyType k)
  690. {
  691. return input->wasKeyPressed(k);
  692. }
  693. inline bool wasKeyReleased(GameKeyType k)
  694. {
  695. return input->wasKeyReleased(k);
  696. }
  697. #ifdef __ANDROID__
  698. void handleAndroidChatInput();
  699. #endif
  700. private:
  701. struct Flags {
  702. bool force_fog_off = false;
  703. bool disable_camera_update = false;
  704. };
  705. void showDeathFormspec();
  706. void showPauseMenu();
  707. void pauseAnimation();
  708. void resumeAnimation();
  709. // ClientEvent handlers
  710. void handleClientEvent_None(ClientEvent *event, CameraOrientation *cam);
  711. void handleClientEvent_PlayerDamage(ClientEvent *event, CameraOrientation *cam);
  712. void handleClientEvent_PlayerForceMove(ClientEvent *event, CameraOrientation *cam);
  713. void handleClientEvent_Deathscreen(ClientEvent *event, CameraOrientation *cam);
  714. void handleClientEvent_ShowFormSpec(ClientEvent *event, CameraOrientation *cam);
  715. void handleClientEvent_ShowLocalFormSpec(ClientEvent *event, CameraOrientation *cam);
  716. void handleClientEvent_HandleParticleEvent(ClientEvent *event,
  717. CameraOrientation *cam);
  718. void handleClientEvent_HudAdd(ClientEvent *event, CameraOrientation *cam);
  719. void handleClientEvent_HudRemove(ClientEvent *event, CameraOrientation *cam);
  720. void handleClientEvent_HudChange(ClientEvent *event, CameraOrientation *cam);
  721. void handleClientEvent_SetSky(ClientEvent *event, CameraOrientation *cam);
  722. void handleClientEvent_SetSun(ClientEvent *event, CameraOrientation *cam);
  723. void handleClientEvent_SetMoon(ClientEvent *event, CameraOrientation *cam);
  724. void handleClientEvent_SetStars(ClientEvent *event, CameraOrientation *cam);
  725. void handleClientEvent_OverrideDayNigthRatio(ClientEvent *event,
  726. CameraOrientation *cam);
  727. void handleClientEvent_CloudParams(ClientEvent *event, CameraOrientation *cam);
  728. void updateChat(f32 dtime);
  729. bool nodePlacement(const ItemDefinition &selected_def, const ItemStack &selected_item,
  730. const v3s16 &nodepos, const v3s16 &neighborpos, const PointedThing &pointed,
  731. const NodeMetadata *meta);
  732. static const ClientEventHandler clientEventHandler[CLIENTEVENT_MAX];
  733. f32 getSensitivityScaleFactor() const;
  734. InputHandler *input = nullptr;
  735. Client *client = nullptr;
  736. Server *server = nullptr;
  737. ClientDynamicInfo client_display_info{};
  738. float dynamic_info_send_timer = 0;
  739. IWritableTextureSource *texture_src = nullptr;
  740. IWritableShaderSource *shader_src = nullptr;
  741. // When created, these will be filled with data received from the server
  742. IWritableItemDefManager *itemdef_manager = nullptr;
  743. NodeDefManager *nodedef_manager = nullptr;
  744. std::unique_ptr<ISoundManager> sound_manager;
  745. SoundMaker *soundmaker = nullptr;
  746. ChatBackend *chat_backend = nullptr;
  747. LogOutputBuffer m_chat_log_buf;
  748. EventManager *eventmgr = nullptr;
  749. QuicktuneShortcutter *quicktune = nullptr;
  750. std::unique_ptr<GameUI> m_game_ui;
  751. GUIChatConsole *gui_chat_console = nullptr; // Free using ->Drop()
  752. MapDrawControl *draw_control = nullptr;
  753. Camera *camera = nullptr;
  754. Clouds *clouds = nullptr; // Free using ->Drop()
  755. Sky *sky = nullptr; // Free using ->Drop()
  756. Hud *hud = nullptr;
  757. Minimap *mapper = nullptr;
  758. // Map server hud ids to client hud ids
  759. std::unordered_map<u32, u32> m_hud_server_to_client;
  760. GameRunData runData;
  761. Flags m_flags;
  762. /* 'cache'
  763. This class does take ownership/responsibily for cleaning up etc of any of
  764. these items (e.g. device)
  765. */
  766. IrrlichtDevice *device;
  767. RenderingEngine *m_rendering_engine;
  768. video::IVideoDriver *driver;
  769. scene::ISceneManager *smgr;
  770. bool *kill;
  771. std::string *error_message;
  772. bool *reconnect_requested;
  773. PausedNodesList paused_animated_nodes;
  774. bool simple_singleplayer_mode;
  775. /* End 'cache' */
  776. /* Pre-calculated values
  777. */
  778. int crack_animation_length;
  779. IntervalLimiter profiler_interval;
  780. /*
  781. * TODO: Local caching of settings is not optimal and should at some stage
  782. * be updated to use a global settings object for getting thse values
  783. * (as opposed to the this local caching). This can be addressed in
  784. * a later release.
  785. */
  786. bool m_cache_doubletap_jump;
  787. bool m_cache_enable_clouds;
  788. bool m_cache_enable_joysticks;
  789. bool m_cache_enable_particles;
  790. bool m_cache_enable_fog;
  791. bool m_cache_enable_noclip;
  792. bool m_cache_enable_free_move;
  793. f32 m_cache_mouse_sensitivity;
  794. f32 m_cache_joystick_frustum_sensitivity;
  795. f32 m_repeat_place_time;
  796. f32 m_cache_cam_smoothing;
  797. bool m_invert_mouse;
  798. bool m_enable_hotbar_mouse_wheel;
  799. bool m_invert_hotbar_mouse_wheel;
  800. bool m_first_loop_after_window_activation = false;
  801. bool m_camera_offset_changed = false;
  802. bool m_game_focused = false;
  803. bool m_does_lost_focus_pause_game = false;
  804. // if true, (almost) the whole game is paused
  805. // this happens in pause menu in singleplayer
  806. bool m_is_paused = false;
  807. bool m_touch_simulate_aux1 = false;
  808. bool m_touch_use_crosshair;
  809. inline bool isTouchCrosshairDisabled() {
  810. return !m_touch_use_crosshair && camera->getCameraMode() == CAMERA_MODE_FIRST;
  811. }
  812. #ifdef __ANDROID__
  813. bool m_android_chat_open;
  814. #endif
  815. };
  816. Game::Game() :
  817. m_chat_log_buf(g_logger),
  818. m_game_ui(new GameUI())
  819. {
  820. g_settings->registerChangedCallback("doubletap_jump",
  821. &settingChangedCallback, this);
  822. g_settings->registerChangedCallback("enable_clouds",
  823. &settingChangedCallback, this);
  824. g_settings->registerChangedCallback("enable_joysticks",
  825. &settingChangedCallback, this);
  826. g_settings->registerChangedCallback("enable_particles",
  827. &settingChangedCallback, this);
  828. g_settings->registerChangedCallback("enable_fog",
  829. &settingChangedCallback, this);
  830. g_settings->registerChangedCallback("mouse_sensitivity",
  831. &settingChangedCallback, this);
  832. g_settings->registerChangedCallback("joystick_frustum_sensitivity",
  833. &settingChangedCallback, this);
  834. g_settings->registerChangedCallback("repeat_place_time",
  835. &settingChangedCallback, this);
  836. g_settings->registerChangedCallback("noclip",
  837. &settingChangedCallback, this);
  838. g_settings->registerChangedCallback("free_move",
  839. &settingChangedCallback, this);
  840. g_settings->registerChangedCallback("fog_start",
  841. &settingChangedCallback, this);
  842. g_settings->registerChangedCallback("cinematic",
  843. &settingChangedCallback, this);
  844. g_settings->registerChangedCallback("cinematic_camera_smoothing",
  845. &settingChangedCallback, this);
  846. g_settings->registerChangedCallback("camera_smoothing",
  847. &settingChangedCallback, this);
  848. g_settings->registerChangedCallback("invert_mouse",
  849. &settingChangedCallback, this);
  850. g_settings->registerChangedCallback("enable_hotbar_mouse_wheel",
  851. &settingChangedCallback, this);
  852. g_settings->registerChangedCallback("invert_hotbar_mouse_wheel",
  853. &settingChangedCallback, this);
  854. g_settings->registerChangedCallback("pause_on_lost_focus",
  855. &settingChangedCallback, this);
  856. readSettings();
  857. }
  858. /****************************************************************************
  859. MinetestApp Public
  860. ****************************************************************************/
  861. Game::~Game()
  862. {
  863. delete client;
  864. delete soundmaker;
  865. sound_manager.reset();
  866. delete server; // deleted first to stop all server threads
  867. delete hud;
  868. delete camera;
  869. delete quicktune;
  870. delete eventmgr;
  871. delete texture_src;
  872. delete shader_src;
  873. delete nodedef_manager;
  874. delete itemdef_manager;
  875. delete draw_control;
  876. clearTextureNameCache();
  877. g_settings->deregisterChangedCallback("doubletap_jump",
  878. &settingChangedCallback, this);
  879. g_settings->deregisterChangedCallback("enable_clouds",
  880. &settingChangedCallback, this);
  881. g_settings->deregisterChangedCallback("enable_joysticks",
  882. &settingChangedCallback, this);
  883. g_settings->deregisterChangedCallback("enable_particles",
  884. &settingChangedCallback, this);
  885. g_settings->deregisterChangedCallback("enable_fog",
  886. &settingChangedCallback, this);
  887. g_settings->deregisterChangedCallback("mouse_sensitivity",
  888. &settingChangedCallback, this);
  889. g_settings->deregisterChangedCallback("joystick_frustum_sensitivity",
  890. &settingChangedCallback, this);
  891. g_settings->deregisterChangedCallback("repeat_place_time",
  892. &settingChangedCallback, this);
  893. g_settings->deregisterChangedCallback("noclip",
  894. &settingChangedCallback, this);
  895. g_settings->deregisterChangedCallback("free_move",
  896. &settingChangedCallback, this);
  897. g_settings->deregisterChangedCallback("fog_start",
  898. &settingChangedCallback, this);
  899. g_settings->deregisterChangedCallback("cinematic",
  900. &settingChangedCallback, this);
  901. g_settings->deregisterChangedCallback("cinematic_camera_smoothing",
  902. &settingChangedCallback, this);
  903. g_settings->deregisterChangedCallback("camera_smoothing",
  904. &settingChangedCallback, this);
  905. g_settings->deregisterChangedCallback("invert_mouse",
  906. &settingChangedCallback, this);
  907. g_settings->deregisterChangedCallback("enable_hotbar_mouse_wheel",
  908. &settingChangedCallback, this);
  909. g_settings->deregisterChangedCallback("invert_hotbar_mouse_wheel",
  910. &settingChangedCallback, this);
  911. g_settings->deregisterChangedCallback("pause_on_lost_focus",
  912. &settingChangedCallback, this);
  913. if (m_rendering_engine)
  914. m_rendering_engine->finalize();
  915. }
  916. bool Game::startup(bool *kill,
  917. InputHandler *input,
  918. RenderingEngine *rendering_engine,
  919. const GameStartData &start_data,
  920. std::string &error_message,
  921. bool *reconnect,
  922. ChatBackend *chat_backend)
  923. {
  924. // "cache"
  925. m_rendering_engine = rendering_engine;
  926. device = m_rendering_engine->get_raw_device();
  927. this->kill = kill;
  928. this->error_message = &error_message;
  929. reconnect_requested = reconnect;
  930. this->input = input;
  931. this->chat_backend = chat_backend;
  932. simple_singleplayer_mode = start_data.isSinglePlayer();
  933. input->keycache.populate();
  934. driver = device->getVideoDriver();
  935. smgr = m_rendering_engine->get_scene_manager();
  936. driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, g_settings->getBool("mip_map"));
  937. smgr->getParameters()->setAttribute(scene::OBJ_LOADER_IGNORE_MATERIAL_FILES, true);
  938. // Reinit runData
  939. runData = GameRunData();
  940. runData.time_from_last_punch = 10.0;
  941. m_game_ui->initFlags();
  942. m_first_loop_after_window_activation = true;
  943. m_touch_use_crosshair = g_settings->getBool("touch_use_crosshair");
  944. g_client_translations->clear();
  945. // address can change if simple_singleplayer_mode
  946. if (!init(start_data.world_spec.path, start_data.address,
  947. start_data.socket_port, start_data.game_spec))
  948. return false;
  949. if (!createClient(start_data))
  950. return false;
  951. m_rendering_engine->initialize(client, hud);
  952. return true;
  953. }
  954. void Game::run()
  955. {
  956. ProfilerGraph graph;
  957. RunStats stats = {};
  958. CameraOrientation cam_view_target = {};
  959. CameraOrientation cam_view = {};
  960. FpsControl draw_times;
  961. f32 dtime; // in seconds
  962. /* Clear the profiler */
  963. Profiler::GraphValues dummyvalues;
  964. g_profiler->graphGet(dummyvalues);
  965. draw_times.reset();
  966. set_light_table(g_settings->getFloat("display_gamma"));
  967. m_touch_simulate_aux1 = g_settings->getBool("fast_move")
  968. && client->checkPrivilege("fast");
  969. const irr::core::dimension2du initial_screen_size(
  970. g_settings->getU16("screen_w"),
  971. g_settings->getU16("screen_h")
  972. );
  973. const bool initial_window_maximized = g_settings->getBool("window_maximized");
  974. while (m_rendering_engine->run()
  975. && !(*kill || g_gamecallback->shutdown_requested
  976. || (server && server->isShutdownRequested()))) {
  977. // Calculate dtime =
  978. // m_rendering_engine->run() from this iteration
  979. // + Sleep time until the wanted FPS are reached
  980. draw_times.limit(device, &dtime, g_menumgr.pausesGame());
  981. const auto current_dynamic_info = ClientDynamicInfo::getCurrent();
  982. if (!current_dynamic_info.equal(client_display_info)) {
  983. client_display_info = current_dynamic_info;
  984. dynamic_info_send_timer = 0.2f;
  985. }
  986. if (dynamic_info_send_timer > 0.0f) {
  987. dynamic_info_send_timer -= dtime;
  988. if (dynamic_info_send_timer <= 0.0f) {
  989. client->sendUpdateClientInfo(current_dynamic_info);
  990. }
  991. }
  992. // Prepare render data for next iteration
  993. updateStats(&stats, draw_times, dtime);
  994. updateInteractTimers(dtime);
  995. if (!checkConnection())
  996. break;
  997. if (!handleCallbacks())
  998. break;
  999. processQueues();
  1000. m_game_ui->clearInfoText();
  1001. updateProfilers(stats, draw_times, dtime);
  1002. processUserInput(dtime);
  1003. // Update camera before player movement to avoid camera lag of one frame
  1004. updateCameraDirection(&cam_view_target, dtime);
  1005. cam_view.camera_yaw += (cam_view_target.camera_yaw -
  1006. cam_view.camera_yaw) * m_cache_cam_smoothing;
  1007. cam_view.camera_pitch += (cam_view_target.camera_pitch -
  1008. cam_view.camera_pitch) * m_cache_cam_smoothing;
  1009. updatePlayerControl(cam_view);
  1010. updatePauseState();
  1011. if (m_is_paused)
  1012. dtime = 0.0f;
  1013. step(dtime);
  1014. processClientEvents(&cam_view_target);
  1015. updateDebugState();
  1016. updateCamera(dtime);
  1017. updateSound(dtime);
  1018. processPlayerInteraction(dtime, m_game_ui->m_flags.show_hud);
  1019. updateFrame(&graph, &stats, dtime, cam_view);
  1020. updateProfilerGraphs(&graph);
  1021. if (m_does_lost_focus_pause_game && !device->isWindowFocused() && !isMenuActive()) {
  1022. showPauseMenu();
  1023. }
  1024. }
  1025. RenderingEngine::autosaveScreensizeAndCo(initial_screen_size, initial_window_maximized);
  1026. }
  1027. void Game::shutdown()
  1028. {
  1029. auto formspec = m_game_ui->getFormspecGUI();
  1030. if (formspec)
  1031. formspec->quitMenu();
  1032. // Clear text when exiting.
  1033. m_game_ui->clearText();
  1034. if (g_touchscreengui)
  1035. g_touchscreengui->hide();
  1036. showOverlayMessage(N_("Shutting down..."), 0, 0, false);
  1037. if (clouds)
  1038. clouds->drop();
  1039. if (gui_chat_console)
  1040. gui_chat_console->drop();
  1041. if (sky)
  1042. sky->drop();
  1043. /* cleanup menus */
  1044. while (g_menumgr.menuCount() > 0) {
  1045. g_menumgr.m_stack.front()->setVisible(false);
  1046. g_menumgr.deletingMenu(g_menumgr.m_stack.front());
  1047. }
  1048. m_game_ui->deleteFormspec();
  1049. chat_backend->addMessage(L"", L"# Disconnected.");
  1050. chat_backend->addMessage(L"", L"");
  1051. m_chat_log_buf.clear();
  1052. if (client) {
  1053. client->Stop();
  1054. while (!client->isShutdown()) {
  1055. assert(texture_src != NULL);
  1056. assert(shader_src != NULL);
  1057. texture_src->processQueue();
  1058. shader_src->processQueue();
  1059. sleep_ms(100);
  1060. }
  1061. }
  1062. }
  1063. /****************************************************************************/
  1064. /****************************************************************************
  1065. Startup
  1066. ****************************************************************************/
  1067. /****************************************************************************/
  1068. bool Game::init(
  1069. const std::string &map_dir,
  1070. const std::string &address,
  1071. u16 port,
  1072. const SubgameSpec &gamespec)
  1073. {
  1074. texture_src = createTextureSource();
  1075. showOverlayMessage(N_("Loading..."), 0, 0);
  1076. shader_src = createShaderSource();
  1077. itemdef_manager = createItemDefManager();
  1078. nodedef_manager = createNodeDefManager();
  1079. eventmgr = new EventManager();
  1080. quicktune = new QuicktuneShortcutter();
  1081. if (!(texture_src && shader_src && itemdef_manager && nodedef_manager
  1082. && eventmgr && quicktune))
  1083. return false;
  1084. if (!initSound())
  1085. return false;
  1086. // Create a server if not connecting to an existing one
  1087. if (address.empty()) {
  1088. if (!createSingleplayerServer(map_dir, gamespec, port))
  1089. return false;
  1090. }
  1091. return true;
  1092. }
  1093. bool Game::initSound()
  1094. {
  1095. #if USE_SOUND
  1096. if (g_settings->getBool("enable_sound") && g_sound_manager_singleton.get()) {
  1097. infostream << "Attempting to use OpenAL audio" << std::endl;
  1098. sound_manager = createOpenALSoundManager(g_sound_manager_singleton.get(),
  1099. std::make_unique<SoundFallbackPathProvider>());
  1100. if (!sound_manager)
  1101. infostream << "Failed to initialize OpenAL audio" << std::endl;
  1102. } else {
  1103. infostream << "Sound disabled." << std::endl;
  1104. }
  1105. #endif
  1106. if (!sound_manager) {
  1107. infostream << "Using dummy audio." << std::endl;
  1108. sound_manager = std::make_unique<DummySoundManager>();
  1109. }
  1110. soundmaker = new SoundMaker(sound_manager.get(), nodedef_manager);
  1111. if (!soundmaker)
  1112. return false;
  1113. soundmaker->registerReceiver(eventmgr);
  1114. return true;
  1115. }
  1116. bool Game::createSingleplayerServer(const std::string &map_dir,
  1117. const SubgameSpec &gamespec, u16 port)
  1118. {
  1119. showOverlayMessage(N_("Creating server..."), 0, 5);
  1120. std::string bind_str;
  1121. if (simple_singleplayer_mode) {
  1122. // Make the simple singleplayer server only accept connections from localhost,
  1123. // which also makes Windows Defender not show a warning.
  1124. bind_str = "127.0.0.1";
  1125. } else {
  1126. bind_str = g_settings->get("bind_address");
  1127. }
  1128. Address bind_addr(0, 0, 0, 0, port);
  1129. if (g_settings->getBool("ipv6_server"))
  1130. bind_addr.setAddress(static_cast<IPv6AddressBytes*>(nullptr));
  1131. try {
  1132. bind_addr.Resolve(bind_str.c_str());
  1133. } catch (const ResolveError &e) {
  1134. warningstream << "Resolving bind address \"" << bind_str
  1135. << "\" failed: " << e.what()
  1136. << " -- Listening on all addresses." << std::endl;
  1137. }
  1138. if (bind_addr.isIPv6() && !g_settings->getBool("enable_ipv6")) {
  1139. *error_message = fmtgettext("Unable to listen on %s because IPv6 is disabled",
  1140. bind_addr.serializeString().c_str());
  1141. errorstream << *error_message << std::endl;
  1142. return false;
  1143. }
  1144. server = new Server(map_dir, gamespec, simple_singleplayer_mode, bind_addr,
  1145. false, nullptr, error_message);
  1146. server->start();
  1147. copyServerClientCache();
  1148. return true;
  1149. }
  1150. void Game::copyServerClientCache()
  1151. {
  1152. // It would be possible to let the client directly read the media files
  1153. // from where the server knows they are. But aside from being more complicated
  1154. // it would also *not* fill the media cache and cause slower joining of
  1155. // remote servers.
  1156. // (Imagine that you launch a game once locally and then connect to a server.)
  1157. assert(server);
  1158. auto map = server->getMediaList();
  1159. u32 n = 0;
  1160. for (auto &it : map) {
  1161. assert(it.first.size() == 20); // SHA1
  1162. if (clientMediaUpdateCacheCopy(it.first, it.second))
  1163. n++;
  1164. }
  1165. infostream << "Copied " << n << " files directly from server to client cache"
  1166. << std::endl;
  1167. }
  1168. bool Game::createClient(const GameStartData &start_data)
  1169. {
  1170. showOverlayMessage(N_("Creating client..."), 0, 10);
  1171. draw_control = new MapDrawControl();
  1172. if (!draw_control)
  1173. return false;
  1174. bool could_connect, connect_aborted;
  1175. if (!connectToServer(start_data, &could_connect, &connect_aborted))
  1176. return false;
  1177. if (!could_connect) {
  1178. if (error_message->empty() && !connect_aborted) {
  1179. // Should not happen if error messages are set properly
  1180. *error_message = gettext("Connection failed for unknown reason");
  1181. errorstream << *error_message << std::endl;
  1182. }
  1183. return false;
  1184. }
  1185. if (!getServerContent(&connect_aborted)) {
  1186. if (error_message->empty() && !connect_aborted) {
  1187. // Should not happen if error messages are set properly
  1188. *error_message = gettext("Connection failed for unknown reason");
  1189. errorstream << *error_message << std::endl;
  1190. }
  1191. return false;
  1192. }
  1193. auto *scsf = new GameGlobalShaderConstantSetterFactory(client);
  1194. shader_src->addShaderConstantSetterFactory(scsf);
  1195. shader_src->addShaderConstantSetterFactory(
  1196. new FogShaderConstantSetterFactory());
  1197. ShadowRenderer::preInit(shader_src);
  1198. // Update cached textures, meshes and materials
  1199. client->afterContentReceived();
  1200. /* Camera
  1201. */
  1202. camera = new Camera(*draw_control, client, m_rendering_engine);
  1203. if (client->modsLoaded())
  1204. client->getScript()->on_camera_ready(camera);
  1205. client->setCamera(camera);
  1206. if (g_touchscreengui) {
  1207. g_touchscreengui->setUseCrosshair(!isTouchCrosshairDisabled());
  1208. }
  1209. /* Clouds
  1210. */
  1211. if (m_cache_enable_clouds)
  1212. clouds = new Clouds(smgr, shader_src, -1, rand());
  1213. /* Skybox
  1214. */
  1215. sky = new Sky(-1, m_rendering_engine, texture_src, shader_src);
  1216. scsf->setSky(sky);
  1217. /* Pre-calculated values
  1218. */
  1219. video::ITexture *t = texture_src->getTexture("crack_anylength.png");
  1220. if (t) {
  1221. v2u32 size = t->getOriginalSize();
  1222. crack_animation_length = size.Y / size.X;
  1223. } else {
  1224. crack_animation_length = 5;
  1225. }
  1226. if (!initGui())
  1227. return false;
  1228. /* Set window caption
  1229. */
  1230. #if IRRLICHT_VERSION_MT_REVISION >= 15
  1231. auto driver_name = driver->getName();
  1232. #else
  1233. auto driver_name = wide_to_utf8(driver->getName());
  1234. #endif
  1235. std::string str = std::string(PROJECT_NAME_C) +
  1236. " " + g_version_hash + " [";
  1237. str += simple_singleplayer_mode ? gettext("Singleplayer")
  1238. : gettext("Multiplayer");
  1239. str += "] [";
  1240. str += driver_name;
  1241. str += "]";
  1242. device->setWindowCaption(utf8_to_wide(str).c_str());
  1243. LocalPlayer *player = client->getEnv().getLocalPlayer();
  1244. player->hurt_tilt_timer = 0;
  1245. player->hurt_tilt_strength = 0;
  1246. hud = new Hud(client, player, &player->inventory);
  1247. mapper = client->getMinimap();
  1248. if (mapper && client->modsLoaded())
  1249. client->getScript()->on_minimap_ready(mapper);
  1250. return true;
  1251. }
  1252. bool Game::initGui()
  1253. {
  1254. m_game_ui->init();
  1255. // Remove stale "recent" chat messages from previous connections
  1256. chat_backend->clearRecentChat();
  1257. // Make sure the size of the recent messages buffer is right
  1258. chat_backend->applySettings();
  1259. // Chat backend and console
  1260. gui_chat_console = new GUIChatConsole(guienv, guienv->getRootGUIElement(),
  1261. -1, chat_backend, client, &g_menumgr);
  1262. if (g_touchscreengui)
  1263. g_touchscreengui->init(texture_src);
  1264. return true;
  1265. }
  1266. bool Game::connectToServer(const GameStartData &start_data,
  1267. bool *connect_ok, bool *connection_aborted)
  1268. {
  1269. *connect_ok = false; // Let's not be overly optimistic
  1270. *connection_aborted = false;
  1271. bool local_server_mode = false;
  1272. const auto &address_name = start_data.address;
  1273. showOverlayMessage(N_("Resolving address..."), 0, 15);
  1274. Address connect_address(0, 0, 0, 0, start_data.socket_port);
  1275. Address fallback_address;
  1276. try {
  1277. connect_address.Resolve(address_name.c_str(), &fallback_address);
  1278. if (connect_address.isAny()) {
  1279. // replace with localhost IP
  1280. if (connect_address.isIPv6()) {
  1281. IPv6AddressBytes addr_bytes;
  1282. addr_bytes.bytes[15] = 1;
  1283. connect_address.setAddress(&addr_bytes);
  1284. } else {
  1285. connect_address.setAddress(127, 0, 0, 1);
  1286. }
  1287. local_server_mode = true;
  1288. }
  1289. } catch (ResolveError &e) {
  1290. *error_message = fmtgettext("Couldn't resolve address: %s", e.what());
  1291. errorstream << *error_message << std::endl;
  1292. return false;
  1293. }
  1294. // this shouldn't normally happen since Address::Resolve() checks for enable_ipv6
  1295. if (g_settings->getBool("enable_ipv6")) {
  1296. // empty
  1297. } else if (connect_address.isIPv6()) {
  1298. *error_message = fmtgettext("Unable to connect to %s because IPv6 is disabled", connect_address.serializeString().c_str());
  1299. errorstream << *error_message << std::endl;
  1300. return false;
  1301. } else if (fallback_address.isIPv6()) {
  1302. fallback_address = Address();
  1303. }
  1304. fallback_address.setPort(connect_address.getPort());
  1305. if (fallback_address.isValid()) {
  1306. infostream << "Resolved two addresses for \"" << address_name
  1307. << "\" isIPv6[0]=" << connect_address.isIPv6()
  1308. << " isIPv6[1]=" << fallback_address.isIPv6() << std::endl;
  1309. } else {
  1310. infostream << "Resolved one address for \"" << address_name
  1311. << "\" isIPv6=" << connect_address.isIPv6() << std::endl;
  1312. }
  1313. try {
  1314. client = new Client(start_data.name.c_str(),
  1315. start_data.password,
  1316. *draw_control, texture_src, shader_src,
  1317. itemdef_manager, nodedef_manager, sound_manager.get(), eventmgr,
  1318. m_rendering_engine, m_game_ui.get(),
  1319. start_data.allow_login_or_register);
  1320. } catch (const BaseException &e) {
  1321. *error_message = fmtgettext("Error creating client: %s", e.what());
  1322. errorstream << *error_message << std::endl;
  1323. return false;
  1324. }
  1325. client->migrateModStorage();
  1326. client->m_simple_singleplayer_mode = simple_singleplayer_mode;
  1327. /*
  1328. Wait for server to accept connection
  1329. */
  1330. client->connect(connect_address, address_name,
  1331. simple_singleplayer_mode || local_server_mode);
  1332. try {
  1333. input->clear();
  1334. FpsControl fps_control;
  1335. f32 dtime;
  1336. f32 wait_time = 0; // in seconds
  1337. bool did_fallback = false;
  1338. fps_control.reset();
  1339. while (m_rendering_engine->run()) {
  1340. fps_control.limit(device, &dtime);
  1341. // Update client and server
  1342. step(dtime);
  1343. // End condition
  1344. if (client->getState() == LC_Init) {
  1345. *connect_ok = true;
  1346. break;
  1347. }
  1348. // Break conditions
  1349. if (*connection_aborted)
  1350. break;
  1351. if (client->accessDenied()) {
  1352. *error_message = fmtgettext("Access denied. Reason: %s", client->accessDeniedReason().c_str());
  1353. *reconnect_requested = client->reconnectRequested();
  1354. errorstream << *error_message << std::endl;
  1355. break;
  1356. }
  1357. if (input->cancelPressed()) {
  1358. *connection_aborted = true;
  1359. infostream << "Connect aborted [Escape]" << std::endl;
  1360. break;
  1361. }
  1362. wait_time += dtime;
  1363. if (local_server_mode) {
  1364. // never time out
  1365. } else if (wait_time > GAME_FALLBACK_TIMEOUT && !did_fallback) {
  1366. if (!client->hasServerReplied() && fallback_address.isValid()) {
  1367. client->connect(fallback_address, address_name,
  1368. simple_singleplayer_mode || local_server_mode);
  1369. }
  1370. did_fallback = true;
  1371. } else if (wait_time > GAME_CONNECTION_TIMEOUT) {
  1372. *error_message = gettext("Connection timed out.");
  1373. errorstream << *error_message << std::endl;
  1374. break;
  1375. }
  1376. // Update status
  1377. showOverlayMessage(N_("Connecting to server..."), dtime, 20);
  1378. }
  1379. } catch (con::PeerNotFoundException &e) {
  1380. warningstream << "This should not happen. Please report a bug." << std::endl;
  1381. return false;
  1382. }
  1383. return true;
  1384. }
  1385. bool Game::getServerContent(bool *aborted)
  1386. {
  1387. input->clear();
  1388. FpsControl fps_control;
  1389. f32 dtime; // in seconds
  1390. fps_control.reset();
  1391. while (m_rendering_engine->run()) {
  1392. fps_control.limit(device, &dtime);
  1393. // Update client and server
  1394. step(dtime);
  1395. // End condition
  1396. if (client->mediaReceived() && client->itemdefReceived() &&
  1397. client->nodedefReceived()) {
  1398. return true;
  1399. }
  1400. // Error conditions
  1401. if (!checkConnection())
  1402. return false;
  1403. if (client->getState() < LC_Init) {
  1404. *error_message = gettext("Client disconnected");
  1405. errorstream << *error_message << std::endl;
  1406. return false;
  1407. }
  1408. if (input->cancelPressed()) {
  1409. *aborted = true;
  1410. infostream << "Connect aborted [Escape]" << std::endl;
  1411. return false;
  1412. }
  1413. // Display status
  1414. int progress = 25;
  1415. if (!client->itemdefReceived()) {
  1416. progress = 25;
  1417. m_rendering_engine->draw_load_screen(wstrgettext("Item definitions..."),
  1418. guienv, texture_src, dtime, progress);
  1419. } else if (!client->nodedefReceived()) {
  1420. progress = 30;
  1421. m_rendering_engine->draw_load_screen(wstrgettext("Node definitions..."),
  1422. guienv, texture_src, dtime, progress);
  1423. } else {
  1424. std::ostringstream message;
  1425. std::fixed(message);
  1426. message.precision(0);
  1427. float receive = client->mediaReceiveProgress() * 100;
  1428. message << gettext("Media...");
  1429. if (receive > 0)
  1430. message << " " << receive << "%";
  1431. message.precision(2);
  1432. if ((USE_CURL == 0) ||
  1433. (!g_settings->getBool("enable_remote_media_server"))) {
  1434. float cur = client->getCurRate();
  1435. std::string cur_unit = gettext("KiB/s");
  1436. if (cur > 900) {
  1437. cur /= 1024.0;
  1438. cur_unit = gettext("MiB/s");
  1439. }
  1440. message << " (" << cur << ' ' << cur_unit << ")";
  1441. }
  1442. progress = 30 + client->mediaReceiveProgress() * 35 + 0.5;
  1443. m_rendering_engine->draw_load_screen(utf8_to_wide(message.str()), guienv,
  1444. texture_src, dtime, progress);
  1445. }
  1446. }
  1447. *aborted = true;
  1448. infostream << "Connect aborted [device]" << std::endl;
  1449. return false;
  1450. }
  1451. /****************************************************************************/
  1452. /****************************************************************************
  1453. Run
  1454. ****************************************************************************/
  1455. /****************************************************************************/
  1456. inline void Game::updateInteractTimers(f32 dtime)
  1457. {
  1458. if (runData.nodig_delay_timer >= 0)
  1459. runData.nodig_delay_timer -= dtime;
  1460. if (runData.object_hit_delay_timer >= 0)
  1461. runData.object_hit_delay_timer -= dtime;
  1462. runData.time_from_last_punch += dtime;
  1463. }
  1464. /* returns false if game should exit, otherwise true
  1465. */
  1466. inline bool Game::checkConnection()
  1467. {
  1468. if (client->accessDenied()) {
  1469. *error_message = fmtgettext("Access denied. Reason: %s", client->accessDeniedReason().c_str());
  1470. *reconnect_requested = client->reconnectRequested();
  1471. errorstream << *error_message << std::endl;
  1472. return false;
  1473. }
  1474. return true;
  1475. }
  1476. /* returns false if game should exit, otherwise true
  1477. */
  1478. inline bool Game::handleCallbacks()
  1479. {
  1480. if (g_gamecallback->disconnect_requested) {
  1481. g_gamecallback->disconnect_requested = false;
  1482. return false;
  1483. }
  1484. if (g_gamecallback->changepassword_requested) {
  1485. (new GUIPasswordChange(guienv, guiroot, -1,
  1486. &g_menumgr, client, texture_src))->drop();
  1487. g_gamecallback->changepassword_requested = false;
  1488. }
  1489. if (g_gamecallback->changevolume_requested) {
  1490. (new GUIVolumeChange(guienv, guiroot, -1,
  1491. &g_menumgr, texture_src))->drop();
  1492. g_gamecallback->changevolume_requested = false;
  1493. }
  1494. if (g_gamecallback->keyconfig_requested) {
  1495. (new GUIKeyChangeMenu(guienv, guiroot, -1,
  1496. &g_menumgr, texture_src))->drop();
  1497. g_gamecallback->keyconfig_requested = false;
  1498. }
  1499. if (g_gamecallback->keyconfig_changed) {
  1500. input->keycache.populate(); // update the cache with new settings
  1501. g_gamecallback->keyconfig_changed = false;
  1502. }
  1503. return true;
  1504. }
  1505. void Game::processQueues()
  1506. {
  1507. texture_src->processQueue();
  1508. shader_src->processQueue();
  1509. }
  1510. void Game::updateDebugState()
  1511. {
  1512. LocalPlayer *player = client->getEnv().getLocalPlayer();
  1513. // debug UI and wireframe
  1514. bool has_debug = client->checkPrivilege("debug");
  1515. bool has_basic_debug = has_debug || (player->hud_flags & HUD_FLAG_BASIC_DEBUG);
  1516. if (m_game_ui->m_flags.show_basic_debug) {
  1517. if (!has_basic_debug)
  1518. m_game_ui->m_flags.show_basic_debug = false;
  1519. } else if (m_game_ui->m_flags.show_minimal_debug) {
  1520. if (has_basic_debug)
  1521. m_game_ui->m_flags.show_basic_debug = true;
  1522. }
  1523. if (!has_basic_debug)
  1524. hud->disableBlockBounds();
  1525. if (!has_debug)
  1526. draw_control->show_wireframe = false;
  1527. // noclip
  1528. draw_control->allow_noclip = m_cache_enable_noclip && client->checkPrivilege("noclip");
  1529. }
  1530. void Game::updateProfilers(const RunStats &stats, const FpsControl &draw_times,
  1531. f32 dtime)
  1532. {
  1533. float profiler_print_interval =
  1534. g_settings->getFloat("profiler_print_interval");
  1535. bool print_to_log = true;
  1536. if (profiler_print_interval == 0) {
  1537. print_to_log = false;
  1538. profiler_print_interval = 3;
  1539. }
  1540. if (profiler_interval.step(dtime, profiler_print_interval)) {
  1541. if (print_to_log) {
  1542. infostream << "Profiler:" << std::endl;
  1543. g_profiler->print(infostream);
  1544. }
  1545. m_game_ui->updateProfiler();
  1546. g_profiler->clear();
  1547. }
  1548. // Update update graphs
  1549. g_profiler->graphAdd("Time non-rendering [us]",
  1550. draw_times.busy_time - stats.drawtime);
  1551. g_profiler->graphAdd("Sleep [us]", draw_times.sleep_time);
  1552. g_profiler->graphAdd("FPS", 1.0f / dtime);
  1553. }
  1554. void Game::updateStats(RunStats *stats, const FpsControl &draw_times,
  1555. f32 dtime)
  1556. {
  1557. f32 jitter;
  1558. Jitter *jp;
  1559. /* Time average and jitter calculation
  1560. */
  1561. jp = &stats->dtime_jitter;
  1562. jp->avg = jp->avg * 0.96 + dtime * 0.04;
  1563. jitter = dtime - jp->avg;
  1564. if (jitter > jp->max)
  1565. jp->max = jitter;
  1566. jp->counter += dtime;
  1567. if (jp->counter > 0.0) {
  1568. jp->counter -= 3.0;
  1569. jp->max_sample = jp->max;
  1570. jp->max_fraction = jp->max_sample / (jp->avg + 0.001);
  1571. jp->max = 0.0;
  1572. }
  1573. /* Busytime average and jitter calculation
  1574. */
  1575. jp = &stats->busy_time_jitter;
  1576. jp->avg = jp->avg + draw_times.getBusyMs() * 0.02;
  1577. jitter = draw_times.getBusyMs() - jp->avg;
  1578. if (jitter > jp->max)
  1579. jp->max = jitter;
  1580. if (jitter < jp->min)
  1581. jp->min = jitter;
  1582. jp->counter += dtime;
  1583. if (jp->counter > 0.0) {
  1584. jp->counter -= 3.0;
  1585. jp->max_sample = jp->max;
  1586. jp->min_sample = jp->min;
  1587. jp->max = 0.0;
  1588. jp->min = 0.0;
  1589. }
  1590. }
  1591. /****************************************************************************
  1592. Input handling
  1593. ****************************************************************************/
  1594. void Game::processUserInput(f32 dtime)
  1595. {
  1596. // Reset input if window not active or some menu is active
  1597. if (!device->isWindowActive() || isMenuActive() || guienv->hasFocus(gui_chat_console)) {
  1598. if (m_game_focused) {
  1599. m_game_focused = false;
  1600. infostream << "Game lost focus" << std::endl;
  1601. input->releaseAllKeys();
  1602. } else {
  1603. input->clear();
  1604. }
  1605. if (g_touchscreengui)
  1606. g_touchscreengui->hide();
  1607. } else {
  1608. if (g_touchscreengui) {
  1609. /* on touchscreengui step may generate own input events which ain't
  1610. * what we want in case we just did clear them */
  1611. g_touchscreengui->show();
  1612. g_touchscreengui->step(dtime);
  1613. }
  1614. m_game_focused = true;
  1615. }
  1616. if (!guienv->hasFocus(gui_chat_console) && gui_chat_console->isOpen()) {
  1617. gui_chat_console->closeConsoleAtOnce();
  1618. }
  1619. // Input handler step() (used by the random input generator)
  1620. input->step(dtime);
  1621. #ifdef __ANDROID__
  1622. auto formspec = m_game_ui->getFormspecGUI();
  1623. if (formspec)
  1624. formspec->getAndroidUIInput();
  1625. else
  1626. handleAndroidChatInput();
  1627. #endif
  1628. // Increase timer for double tap of "keymap_jump"
  1629. if (m_cache_doubletap_jump && runData.jump_timer_up <= 0.2f)
  1630. runData.jump_timer_up += dtime;
  1631. if (m_cache_doubletap_jump && runData.jump_timer_down <= 0.4f)
  1632. runData.jump_timer_down += dtime;
  1633. processKeyInput();
  1634. processItemSelection(&runData.new_playeritem);
  1635. }
  1636. void Game::processKeyInput()
  1637. {
  1638. if (wasKeyDown(KeyType::DROP)) {
  1639. dropSelectedItem(isKeyDown(KeyType::SNEAK));
  1640. } else if (wasKeyDown(KeyType::AUTOFORWARD)) {
  1641. toggleAutoforward();
  1642. } else if (wasKeyDown(KeyType::BACKWARD)) {
  1643. if (g_settings->getBool("continuous_forward"))
  1644. toggleAutoforward();
  1645. } else if (wasKeyDown(KeyType::INVENTORY)) {
  1646. openInventory();
  1647. } else if (input->cancelPressed()) {
  1648. #ifdef __ANDROID__
  1649. m_android_chat_open = false;
  1650. #endif
  1651. if (!gui_chat_console->isOpenInhibited()) {
  1652. showPauseMenu();
  1653. }
  1654. } else if (wasKeyDown(KeyType::CHAT)) {
  1655. openConsole(0.2, L"");
  1656. } else if (wasKeyDown(KeyType::CMD)) {
  1657. openConsole(0.2, L"/");
  1658. } else if (wasKeyDown(KeyType::CMD_LOCAL)) {
  1659. if (client->modsLoaded())
  1660. openConsole(0.2, L".");
  1661. else
  1662. m_game_ui->showTranslatedStatusText("Client side scripting is disabled");
  1663. } else if (wasKeyDown(KeyType::CONSOLE)) {
  1664. openConsole(core::clamp(g_settings->getFloat("console_height"), 0.1f, 1.0f));
  1665. } else if (wasKeyDown(KeyType::FREEMOVE)) {
  1666. toggleFreeMove();
  1667. } else if (wasKeyDown(KeyType::JUMP)) {
  1668. toggleFreeMoveAlt();
  1669. } else if (wasKeyDown(KeyType::PITCHMOVE)) {
  1670. togglePitchMove();
  1671. } else if (wasKeyDown(KeyType::FASTMOVE)) {
  1672. toggleFast();
  1673. } else if (wasKeyDown(KeyType::NOCLIP)) {
  1674. toggleNoClip();
  1675. #if USE_SOUND
  1676. } else if (wasKeyDown(KeyType::MUTE)) {
  1677. if (g_settings->getBool("enable_sound")) {
  1678. bool new_mute_sound = !g_settings->getBool("mute_sound");
  1679. g_settings->setBool("mute_sound", new_mute_sound);
  1680. if (new_mute_sound)
  1681. m_game_ui->showTranslatedStatusText("Sound muted");
  1682. else
  1683. m_game_ui->showTranslatedStatusText("Sound unmuted");
  1684. } else {
  1685. m_game_ui->showTranslatedStatusText("Sound system is disabled");
  1686. }
  1687. } else if (wasKeyDown(KeyType::INC_VOLUME)) {
  1688. if (g_settings->getBool("enable_sound")) {
  1689. float new_volume = g_settings->getFloat("sound_volume", 0.0f, 0.9f) + 0.1f;
  1690. g_settings->setFloat("sound_volume", new_volume);
  1691. std::wstring msg = fwgettext("Volume changed to %d%%", myround(new_volume * 100));
  1692. m_game_ui->showStatusText(msg);
  1693. } else {
  1694. m_game_ui->showTranslatedStatusText("Sound system is disabled");
  1695. }
  1696. } else if (wasKeyDown(KeyType::DEC_VOLUME)) {
  1697. if (g_settings->getBool("enable_sound")) {
  1698. float new_volume = g_settings->getFloat("sound_volume", 0.1f, 1.0f) - 0.1f;
  1699. g_settings->setFloat("sound_volume", new_volume);
  1700. std::wstring msg = fwgettext("Volume changed to %d%%", myround(new_volume * 100));
  1701. m_game_ui->showStatusText(msg);
  1702. } else {
  1703. m_game_ui->showTranslatedStatusText("Sound system is disabled");
  1704. }
  1705. #else
  1706. } else if (wasKeyDown(KeyType::MUTE) || wasKeyDown(KeyType::INC_VOLUME)
  1707. || wasKeyDown(KeyType::DEC_VOLUME)) {
  1708. m_game_ui->showTranslatedStatusText("Sound system is not supported on this build");
  1709. #endif
  1710. } else if (wasKeyDown(KeyType::CINEMATIC)) {
  1711. toggleCinematic();
  1712. } else if (wasKeyPressed(KeyType::SCREENSHOT)) {
  1713. client->makeScreenshot();
  1714. } else if (wasKeyPressed(KeyType::TOGGLE_BLOCK_BOUNDS)) {
  1715. toggleBlockBounds();
  1716. } else if (wasKeyPressed(KeyType::TOGGLE_HUD)) {
  1717. m_game_ui->toggleHud();
  1718. } else if (wasKeyPressed(KeyType::MINIMAP)) {
  1719. toggleMinimap(isKeyDown(KeyType::SNEAK));
  1720. } else if (wasKeyPressed(KeyType::TOGGLE_CHAT)) {
  1721. m_game_ui->toggleChat(client);
  1722. } else if (wasKeyPressed(KeyType::TOGGLE_FOG)) {
  1723. toggleFog();
  1724. } else if (wasKeyDown(KeyType::TOGGLE_UPDATE_CAMERA)) {
  1725. toggleUpdateCamera();
  1726. } else if (wasKeyPressed(KeyType::TOGGLE_DEBUG)) {
  1727. toggleDebug();
  1728. } else if (wasKeyPressed(KeyType::TOGGLE_PROFILER)) {
  1729. m_game_ui->toggleProfiler();
  1730. } else if (wasKeyDown(KeyType::INCREASE_VIEWING_RANGE)) {
  1731. increaseViewRange();
  1732. } else if (wasKeyDown(KeyType::DECREASE_VIEWING_RANGE)) {
  1733. decreaseViewRange();
  1734. } else if (wasKeyPressed(KeyType::RANGESELECT)) {
  1735. toggleFullViewRange();
  1736. } else if (wasKeyDown(KeyType::ZOOM)) {
  1737. checkZoomEnabled();
  1738. } else if (wasKeyDown(KeyType::QUICKTUNE_NEXT)) {
  1739. quicktune->next();
  1740. } else if (wasKeyDown(KeyType::QUICKTUNE_PREV)) {
  1741. quicktune->prev();
  1742. } else if (wasKeyDown(KeyType::QUICKTUNE_INC)) {
  1743. quicktune->inc();
  1744. } else if (wasKeyDown(KeyType::QUICKTUNE_DEC)) {
  1745. quicktune->dec();
  1746. }
  1747. if (!isKeyDown(KeyType::JUMP) && runData.reset_jump_timer) {
  1748. runData.reset_jump_timer = false;
  1749. runData.jump_timer_up = 0.0f;
  1750. }
  1751. if (quicktune->hasMessage()) {
  1752. m_game_ui->showStatusText(utf8_to_wide(quicktune->getMessage()));
  1753. }
  1754. }
  1755. void Game::processItemSelection(u16 *new_playeritem)
  1756. {
  1757. LocalPlayer *player = client->getEnv().getLocalPlayer();
  1758. /* Item selection using mouse wheel
  1759. */
  1760. *new_playeritem = player->getWieldIndex();
  1761. u16 max_item = MYMIN(PLAYER_INVENTORY_SIZE - 1,
  1762. player->hud_hotbar_itemcount - 1);
  1763. s32 wheel = input->getMouseWheel();
  1764. if (!m_enable_hotbar_mouse_wheel)
  1765. wheel = 0;
  1766. if (m_invert_hotbar_mouse_wheel)
  1767. wheel *= -1;
  1768. s32 dir = wheel;
  1769. if (wasKeyDown(KeyType::HOTBAR_NEXT))
  1770. dir = -1;
  1771. if (wasKeyDown(KeyType::HOTBAR_PREV))
  1772. dir = 1;
  1773. if (dir < 0)
  1774. *new_playeritem = *new_playeritem < max_item ? *new_playeritem + 1 : 0;
  1775. else if (dir > 0)
  1776. *new_playeritem = *new_playeritem > 0 ? *new_playeritem - 1 : max_item;
  1777. // else dir == 0
  1778. /* Item selection using hotbar slot keys
  1779. */
  1780. for (u16 i = 0; i <= max_item; i++) {
  1781. if (wasKeyDown((GameKeyType) (KeyType::SLOT_1 + i))) {
  1782. *new_playeritem = i;
  1783. break;
  1784. }
  1785. }
  1786. if (g_touchscreengui) {
  1787. std::optional<u16> selection = g_touchscreengui->getHotbarSelection();
  1788. if (selection)
  1789. *new_playeritem = *selection;
  1790. }
  1791. // Clamp selection again in case it wasn't changed but max_item was
  1792. *new_playeritem = MYMIN(*new_playeritem, max_item);
  1793. }
  1794. void Game::dropSelectedItem(bool single_item)
  1795. {
  1796. IDropAction *a = new IDropAction();
  1797. a->count = single_item ? 1 : 0;
  1798. a->from_inv.setCurrentPlayer();
  1799. a->from_list = "main";
  1800. a->from_i = client->getEnv().getLocalPlayer()->getWieldIndex();
  1801. client->inventoryAction(a);
  1802. }
  1803. void Game::openInventory()
  1804. {
  1805. /*
  1806. * Don't permit to open inventory is CAO or player doesn't exists.
  1807. * This prevent showing an empty inventory at player load
  1808. */
  1809. LocalPlayer *player = client->getEnv().getLocalPlayer();
  1810. if (!player || !player->getCAO())
  1811. return;
  1812. infostream << "Game: Launching inventory" << std::endl;
  1813. PlayerInventoryFormSource *fs_src = new PlayerInventoryFormSource(client);
  1814. InventoryLocation inventoryloc;
  1815. inventoryloc.setCurrentPlayer();
  1816. if (client->modsLoaded() && client->getScript()->on_inventory_open(fs_src->m_client->getInventory(inventoryloc))) {
  1817. delete fs_src;
  1818. return;
  1819. }
  1820. if (fs_src->getForm().empty()) {
  1821. delete fs_src;
  1822. return;
  1823. }
  1824. TextDest *txt_dst = new TextDestPlayerInventory(client);
  1825. auto *&formspec = m_game_ui->updateFormspec("");
  1826. GUIFormSpecMenu::create(formspec, client, m_rendering_engine->get_gui_env(),
  1827. &input->joystick, fs_src, txt_dst, client->getFormspecPrepend(),
  1828. sound_manager.get());
  1829. formspec->setFormSpec(fs_src->getForm(), inventoryloc);
  1830. }
  1831. void Game::openConsole(float scale, const wchar_t *line)
  1832. {
  1833. assert(scale > 0.0f && scale <= 1.0f);
  1834. #ifdef __ANDROID__
  1835. porting::showTextInputDialog("", "", 2);
  1836. m_android_chat_open = true;
  1837. #else
  1838. if (gui_chat_console->isOpenInhibited())
  1839. return;
  1840. gui_chat_console->openConsole(scale);
  1841. if (line) {
  1842. gui_chat_console->setCloseOnEnter(true);
  1843. gui_chat_console->replaceAndAddToHistory(line);
  1844. }
  1845. #endif
  1846. }
  1847. #ifdef __ANDROID__
  1848. void Game::handleAndroidChatInput()
  1849. {
  1850. // It has to be a text input
  1851. if (m_android_chat_open && porting::getLastInputDialogType() == porting::TEXT_INPUT) {
  1852. porting::AndroidDialogState dialogState = porting::getInputDialogState();
  1853. if (dialogState == porting::DIALOG_INPUTTED) {
  1854. std::string text = porting::getInputDialogMessage();
  1855. client->typeChatMessage(utf8_to_wide(text));
  1856. }
  1857. if (dialogState != porting::DIALOG_SHOWN)
  1858. m_android_chat_open = false;
  1859. }
  1860. }
  1861. #endif
  1862. void Game::toggleFreeMove()
  1863. {
  1864. bool free_move = !g_settings->getBool("free_move");
  1865. g_settings->set("free_move", bool_to_cstr(free_move));
  1866. if (free_move) {
  1867. if (client->checkPrivilege("fly")) {
  1868. m_game_ui->showTranslatedStatusText("Fly mode enabled");
  1869. } else {
  1870. m_game_ui->showTranslatedStatusText("Fly mode enabled (note: no 'fly' privilege)");
  1871. }
  1872. } else {
  1873. m_game_ui->showTranslatedStatusText("Fly mode disabled");
  1874. }
  1875. }
  1876. void Game::toggleFreeMoveAlt()
  1877. {
  1878. if (!runData.reset_jump_timer) {
  1879. runData.jump_timer_down_before = runData.jump_timer_down;
  1880. runData.jump_timer_down = 0.0f;
  1881. }
  1882. // key down (0.2 s max.), then key up (0.2 s max.), then key down
  1883. if (m_cache_doubletap_jump && runData.jump_timer_up < 0.2f &&
  1884. runData.jump_timer_down_before < 0.4f) // 0.2 + 0.2
  1885. toggleFreeMove();
  1886. runData.reset_jump_timer = true;
  1887. }
  1888. void Game::togglePitchMove()
  1889. {
  1890. bool pitch_move = !g_settings->getBool("pitch_move");
  1891. g_settings->set("pitch_move", bool_to_cstr(pitch_move));
  1892. if (pitch_move) {
  1893. m_game_ui->showTranslatedStatusText("Pitch move mode enabled");
  1894. } else {
  1895. m_game_ui->showTranslatedStatusText("Pitch move mode disabled");
  1896. }
  1897. }
  1898. void Game::toggleFast()
  1899. {
  1900. bool fast_move = !g_settings->getBool("fast_move");
  1901. bool has_fast_privs = client->checkPrivilege("fast");
  1902. g_settings->set("fast_move", bool_to_cstr(fast_move));
  1903. if (fast_move) {
  1904. if (has_fast_privs) {
  1905. m_game_ui->showTranslatedStatusText("Fast mode enabled");
  1906. } else {
  1907. m_game_ui->showTranslatedStatusText("Fast mode enabled (note: no 'fast' privilege)");
  1908. }
  1909. } else {
  1910. m_game_ui->showTranslatedStatusText("Fast mode disabled");
  1911. }
  1912. m_touch_simulate_aux1 = fast_move && has_fast_privs;
  1913. }
  1914. void Game::toggleNoClip()
  1915. {
  1916. bool noclip = !g_settings->getBool("noclip");
  1917. g_settings->set("noclip", bool_to_cstr(noclip));
  1918. if (noclip) {
  1919. if (client->checkPrivilege("noclip")) {
  1920. m_game_ui->showTranslatedStatusText("Noclip mode enabled");
  1921. } else {
  1922. m_game_ui->showTranslatedStatusText("Noclip mode enabled (note: no 'noclip' privilege)");
  1923. }
  1924. } else {
  1925. m_game_ui->showTranslatedStatusText("Noclip mode disabled");
  1926. }
  1927. }
  1928. void Game::toggleCinematic()
  1929. {
  1930. bool cinematic = !g_settings->getBool("cinematic");
  1931. g_settings->set("cinematic", bool_to_cstr(cinematic));
  1932. if (cinematic)
  1933. m_game_ui->showTranslatedStatusText("Cinematic mode enabled");
  1934. else
  1935. m_game_ui->showTranslatedStatusText("Cinematic mode disabled");
  1936. }
  1937. void Game::toggleBlockBounds()
  1938. {
  1939. LocalPlayer *player = client->getEnv().getLocalPlayer();
  1940. if (!(client->checkPrivilege("debug") || (player->hud_flags & HUD_FLAG_BASIC_DEBUG))) {
  1941. m_game_ui->showTranslatedStatusText("Can't show block bounds (disabled by game or mod)");
  1942. return;
  1943. }
  1944. enum Hud::BlockBoundsMode newmode = hud->toggleBlockBounds();
  1945. switch (newmode) {
  1946. case Hud::BLOCK_BOUNDS_OFF:
  1947. m_game_ui->showTranslatedStatusText("Block bounds hidden");
  1948. break;
  1949. case Hud::BLOCK_BOUNDS_CURRENT:
  1950. m_game_ui->showTranslatedStatusText("Block bounds shown for current block");
  1951. break;
  1952. case Hud::BLOCK_BOUNDS_NEAR:
  1953. m_game_ui->showTranslatedStatusText("Block bounds shown for nearby blocks");
  1954. break;
  1955. case Hud::BLOCK_BOUNDS_MAX:
  1956. m_game_ui->showTranslatedStatusText("Block bounds shown for all blocks");
  1957. break;
  1958. default:
  1959. break;
  1960. }
  1961. }
  1962. // Autoforward by toggling continuous forward.
  1963. void Game::toggleAutoforward()
  1964. {
  1965. bool autorun_enabled = !g_settings->getBool("continuous_forward");
  1966. g_settings->set("continuous_forward", bool_to_cstr(autorun_enabled));
  1967. if (autorun_enabled)
  1968. m_game_ui->showTranslatedStatusText("Automatic forward enabled");
  1969. else
  1970. m_game_ui->showTranslatedStatusText("Automatic forward disabled");
  1971. }
  1972. void Game::toggleMinimap(bool shift_pressed)
  1973. {
  1974. if (!mapper || !m_game_ui->m_flags.show_hud || !g_settings->getBool("enable_minimap"))
  1975. return;
  1976. if (shift_pressed)
  1977. mapper->toggleMinimapShape();
  1978. else
  1979. mapper->nextMode();
  1980. // TODO: When legacy minimap is deprecated, keep only HUD minimap stuff here
  1981. // Not so satisying code to keep compatibility with old fixed mode system
  1982. // -->
  1983. u32 hud_flags = client->getEnv().getLocalPlayer()->hud_flags;
  1984. if (hud_flags & HUD_FLAG_MINIMAP_VISIBLE) {
  1985. // If radar is disabled, try to find a non radar mode or fall back to 0
  1986. if (!(hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE))
  1987. while (mapper->getModeIndex() &&
  1988. mapper->getModeDef().type == MINIMAP_TYPE_RADAR)
  1989. mapper->nextMode();
  1990. }
  1991. // <--
  1992. // End of 'not so satifying code'
  1993. if (hud && hud->hasElementOfType(HUD_ELEM_MINIMAP))
  1994. m_game_ui->showStatusText(utf8_to_wide(mapper->getModeDef().label));
  1995. else
  1996. m_game_ui->showTranslatedStatusText("Minimap currently disabled by game or mod");
  1997. }
  1998. void Game::toggleFog()
  1999. {
  2000. bool fog_enabled = g_settings->getBool("enable_fog");
  2001. g_settings->setBool("enable_fog", !fog_enabled);
  2002. if (fog_enabled)
  2003. m_game_ui->showTranslatedStatusText("Fog disabled");
  2004. else
  2005. m_game_ui->showTranslatedStatusText("Fog enabled");
  2006. }
  2007. void Game::toggleDebug()
  2008. {
  2009. LocalPlayer *player = client->getEnv().getLocalPlayer();
  2010. bool has_debug = client->checkPrivilege("debug");
  2011. bool has_basic_debug = has_debug || (player->hud_flags & HUD_FLAG_BASIC_DEBUG);
  2012. // Initial: No debug info
  2013. // 1x toggle: Debug text
  2014. // 2x toggle: Debug text with profiler graph
  2015. // 3x toggle: Debug text and wireframe (needs "debug" priv)
  2016. // Next toggle: Back to initial
  2017. //
  2018. // The debug text can be in 2 modes: minimal and basic.
  2019. // * Minimal: Only technical client info that not gameplay-relevant
  2020. // * Basic: Info that might give gameplay advantage, e.g. pos, angle
  2021. // Basic mode is used when player has the debug HUD flag set,
  2022. // otherwise the Minimal mode is used.
  2023. if (!m_game_ui->m_flags.show_minimal_debug) {
  2024. m_game_ui->m_flags.show_minimal_debug = true;
  2025. if (has_basic_debug)
  2026. m_game_ui->m_flags.show_basic_debug = true;
  2027. m_game_ui->m_flags.show_profiler_graph = false;
  2028. draw_control->show_wireframe = false;
  2029. m_game_ui->showTranslatedStatusText("Debug info shown");
  2030. } else if (!m_game_ui->m_flags.show_profiler_graph && !draw_control->show_wireframe) {
  2031. if (has_basic_debug)
  2032. m_game_ui->m_flags.show_basic_debug = true;
  2033. m_game_ui->m_flags.show_profiler_graph = true;
  2034. m_game_ui->showTranslatedStatusText("Profiler graph shown");
  2035. } else if (!draw_control->show_wireframe && client->checkPrivilege("debug")) {
  2036. if (has_basic_debug)
  2037. m_game_ui->m_flags.show_basic_debug = true;
  2038. m_game_ui->m_flags.show_profiler_graph = false;
  2039. draw_control->show_wireframe = true;
  2040. m_game_ui->showTranslatedStatusText("Wireframe shown");
  2041. } else {
  2042. m_game_ui->m_flags.show_minimal_debug = false;
  2043. m_game_ui->m_flags.show_basic_debug = false;
  2044. m_game_ui->m_flags.show_profiler_graph = false;
  2045. draw_control->show_wireframe = false;
  2046. if (has_debug) {
  2047. m_game_ui->showTranslatedStatusText("Debug info, profiler graph, and wireframe hidden");
  2048. } else {
  2049. m_game_ui->showTranslatedStatusText("Debug info and profiler graph hidden");
  2050. }
  2051. }
  2052. }
  2053. void Game::toggleUpdateCamera()
  2054. {
  2055. m_flags.disable_camera_update = !m_flags.disable_camera_update;
  2056. if (m_flags.disable_camera_update)
  2057. m_game_ui->showTranslatedStatusText("Camera update disabled");
  2058. else
  2059. m_game_ui->showTranslatedStatusText("Camera update enabled");
  2060. }
  2061. void Game::increaseViewRange()
  2062. {
  2063. s16 range = g_settings->getS16("viewing_range");
  2064. s16 range_new = range + 10;
  2065. s16 server_limit = sky->getFogDistance();
  2066. if (range_new >= 4000) {
  2067. range_new = 4000;
  2068. std::wstring msg = server_limit >= 0 && range_new > server_limit ?
  2069. fwgettext("Viewing range changed to %d (the maximum), but limited to %d by game or mod", range_new, server_limit) :
  2070. fwgettext("Viewing range changed to %d (the maximum)", range_new);
  2071. m_game_ui->showStatusText(msg);
  2072. } else {
  2073. std::wstring msg = server_limit >= 0 && range_new > server_limit ?
  2074. fwgettext("Viewing range changed to %d, but limited to %d by game or mod", range_new, server_limit) :
  2075. fwgettext("Viewing range changed to %d", range_new);
  2076. m_game_ui->showStatusText(msg);
  2077. }
  2078. g_settings->set("viewing_range", itos(range_new));
  2079. }
  2080. void Game::decreaseViewRange()
  2081. {
  2082. s16 range = g_settings->getS16("viewing_range");
  2083. s16 range_new = range - 10;
  2084. s16 server_limit = sky->getFogDistance();
  2085. if (range_new <= 20) {
  2086. range_new = 20;
  2087. std::wstring msg = server_limit >= 0 && range_new > server_limit ?
  2088. fwgettext("Viewing changed to %d (the minimum), but limited to %d by game or mod", range_new, server_limit) :
  2089. fwgettext("Viewing changed to %d (the minimum)", range_new);
  2090. m_game_ui->showStatusText(msg);
  2091. } else {
  2092. std::wstring msg = server_limit >= 0 && range_new > server_limit ?
  2093. fwgettext("Viewing range changed to %d, but limited to %d by game or mod", range_new, server_limit) :
  2094. fwgettext("Viewing range changed to %d", range_new);
  2095. m_game_ui->showStatusText(msg);
  2096. }
  2097. g_settings->set("viewing_range", itos(range_new));
  2098. }
  2099. void Game::toggleFullViewRange()
  2100. {
  2101. draw_control->range_all = !draw_control->range_all;
  2102. if (draw_control->range_all) {
  2103. if (sky->getFogDistance() >= 0) {
  2104. m_game_ui->showTranslatedStatusText("Unlimited viewing range enabled, but forbidden by game or mod");
  2105. } else {
  2106. m_game_ui->showTranslatedStatusText("Unlimited viewing range enabled");
  2107. }
  2108. } else {
  2109. m_game_ui->showTranslatedStatusText("Unlimited viewing range disabled");
  2110. }
  2111. }
  2112. void Game::checkZoomEnabled()
  2113. {
  2114. LocalPlayer *player = client->getEnv().getLocalPlayer();
  2115. if (player->getZoomFOV() < 0.001f || player->getFov().fov > 0.0f)
  2116. m_game_ui->showTranslatedStatusText("Zoom currently disabled by game or mod");
  2117. }
  2118. void Game::updateCameraDirection(CameraOrientation *cam, float dtime)
  2119. {
  2120. auto *cur_control = device->getCursorControl();
  2121. /* With CIrrDeviceSDL on Linux and Windows, enabling relative mouse mode
  2122. somehow results in simulated mouse events being generated from touch events,
  2123. although SDL_HINT_MOUSE_TOUCH_EVENTS and SDL_HINT_TOUCH_MOUSE_EVENTS are set to 0.
  2124. Since Minetest has its own code to synthesize mouse events from touch events,
  2125. this results in duplicated input. To avoid that, we don't enable relative
  2126. mouse mode if we're in touchscreen mode. */
  2127. if (cur_control)
  2128. cur_control->setRelativeMode(!g_touchscreengui && !isMenuActive());
  2129. if ((device->isWindowActive() && device->isWindowFocused()
  2130. && !isMenuActive()) || input->isRandom()) {
  2131. if (cur_control && !input->isRandom()) {
  2132. // Mac OSX gets upset if this is set every frame
  2133. if (cur_control->isVisible())
  2134. cur_control->setVisible(false);
  2135. }
  2136. if (m_first_loop_after_window_activation) {
  2137. m_first_loop_after_window_activation = false;
  2138. input->setMousePos(driver->getScreenSize().Width / 2,
  2139. driver->getScreenSize().Height / 2);
  2140. } else {
  2141. updateCameraOrientation(cam, dtime);
  2142. }
  2143. } else {
  2144. // Mac OSX gets upset if this is set every frame
  2145. if (cur_control && !cur_control->isVisible())
  2146. cur_control->setVisible(true);
  2147. m_first_loop_after_window_activation = true;
  2148. }
  2149. }
  2150. // Get the factor to multiply with sensitivity to get the same mouse/joystick
  2151. // responsiveness independently of FOV.
  2152. f32 Game::getSensitivityScaleFactor() const
  2153. {
  2154. f32 fov_y = client->getCamera()->getFovY();
  2155. // Multiply by a constant such that it becomes 1.0 at 72 degree FOV and
  2156. // 16:9 aspect ratio to minimize disruption of existing sensitivity
  2157. // settings.
  2158. return std::tan(fov_y / 2.0f) * 1.3763819f;
  2159. }
  2160. void Game::updateCameraOrientation(CameraOrientation *cam, float dtime)
  2161. {
  2162. if (g_touchscreengui) {
  2163. cam->camera_yaw += g_touchscreengui->getYawChange();
  2164. cam->camera_pitch += g_touchscreengui->getPitchChange();
  2165. } else {
  2166. v2s32 center(driver->getScreenSize().Width / 2, driver->getScreenSize().Height / 2);
  2167. v2s32 dist = input->getMousePos() - center;
  2168. if (m_invert_mouse || camera->getCameraMode() == CAMERA_MODE_THIRD_FRONT) {
  2169. dist.Y = -dist.Y;
  2170. }
  2171. f32 sens_scale = getSensitivityScaleFactor();
  2172. cam->camera_yaw -= dist.X * m_cache_mouse_sensitivity * sens_scale;
  2173. cam->camera_pitch += dist.Y * m_cache_mouse_sensitivity * sens_scale;
  2174. if (dist.X != 0 || dist.Y != 0)
  2175. input->setMousePos(center.X, center.Y);
  2176. }
  2177. if (m_cache_enable_joysticks) {
  2178. f32 sens_scale = getSensitivityScaleFactor();
  2179. f32 c = m_cache_joystick_frustum_sensitivity * dtime * sens_scale;
  2180. cam->camera_yaw -= input->joystick.getAxisWithoutDead(JA_FRUSTUM_HORIZONTAL) * c;
  2181. cam->camera_pitch += input->joystick.getAxisWithoutDead(JA_FRUSTUM_VERTICAL) * c;
  2182. }
  2183. cam->camera_pitch = rangelim(cam->camera_pitch, -89.5, 89.5);
  2184. }
  2185. void Game::updatePlayerControl(const CameraOrientation &cam)
  2186. {
  2187. LocalPlayer *player = client->getEnv().getLocalPlayer();
  2188. //TimeTaker tt("update player control", NULL, PRECISION_NANO);
  2189. PlayerControl control(
  2190. isKeyDown(KeyType::FORWARD),
  2191. isKeyDown(KeyType::BACKWARD),
  2192. isKeyDown(KeyType::LEFT),
  2193. isKeyDown(KeyType::RIGHT),
  2194. isKeyDown(KeyType::JUMP) || player->getAutojump(),
  2195. isKeyDown(KeyType::AUX1),
  2196. isKeyDown(KeyType::SNEAK),
  2197. isKeyDown(KeyType::ZOOM),
  2198. isKeyDown(KeyType::DIG),
  2199. isKeyDown(KeyType::PLACE),
  2200. cam.camera_pitch,
  2201. cam.camera_yaw,
  2202. input->getMovementSpeed(),
  2203. input->getMovementDirection()
  2204. );
  2205. // autoforward if set: move at maximum speed
  2206. if (player->getPlayerSettings().continuous_forward &&
  2207. client->activeObjectsReceived() && !player->isDead()) {
  2208. control.movement_speed = 1.0f;
  2209. // sideways movement only
  2210. float dx = std::sin(control.movement_direction);
  2211. control.movement_direction = std::atan2(dx, 1.0f);
  2212. }
  2213. /* For touch, simulate holding down AUX1 (fast move) if the user has
  2214. * the fast_move setting toggled on. If there is an aux1 key defined for
  2215. * touch then its meaning is inverted (i.e. holding aux1 means walk and
  2216. * not fast)
  2217. */
  2218. if (g_touchscreengui && m_touch_simulate_aux1) {
  2219. control.aux1 = control.aux1 ^ true;
  2220. }
  2221. client->setPlayerControl(control);
  2222. //tt.stop();
  2223. }
  2224. void Game::updatePauseState()
  2225. {
  2226. bool was_paused = this->m_is_paused;
  2227. this->m_is_paused = this->simple_singleplayer_mode && g_menumgr.pausesGame();
  2228. if (!was_paused && this->m_is_paused) {
  2229. this->pauseAnimation();
  2230. this->sound_manager->pauseAll();
  2231. } else if (was_paused && !this->m_is_paused) {
  2232. this->resumeAnimation();
  2233. this->sound_manager->resumeAll();
  2234. }
  2235. }
  2236. inline void Game::step(f32 dtime)
  2237. {
  2238. if (server) {
  2239. float fps_max = (!device->isWindowFocused() || g_menumgr.pausesGame()) ?
  2240. g_settings->getFloat("fps_max_unfocused") :
  2241. g_settings->getFloat("fps_max");
  2242. fps_max = std::max(fps_max, 1.0f);
  2243. /*
  2244. * Unless you have a barebones game, running the server at more than 60Hz
  2245. * is hardly realistic and you're at the point of diminishing returns.
  2246. * fps_max is also not necessarily anywhere near the FPS actually achieved
  2247. * (also due to vsync).
  2248. */
  2249. fps_max = std::min(fps_max, 60.0f);
  2250. server->setStepSettings(Server::StepSettings{
  2251. 1.0f / fps_max,
  2252. m_is_paused
  2253. });
  2254. server->step();
  2255. }
  2256. if (!m_is_paused)
  2257. client->step(dtime);
  2258. }
  2259. static void pauseNodeAnimation(PausedNodesList &paused, scene::ISceneNode *node) {
  2260. if (!node)
  2261. return;
  2262. for (auto &&child: node->getChildren())
  2263. pauseNodeAnimation(paused, child);
  2264. if (node->getType() != scene::ESNT_ANIMATED_MESH)
  2265. return;
  2266. auto animated_node = static_cast<scene::IAnimatedMeshSceneNode *>(node);
  2267. float speed = animated_node->getAnimationSpeed();
  2268. if (!speed)
  2269. return;
  2270. paused.push_back({grab(animated_node), speed});
  2271. animated_node->setAnimationSpeed(0.0f);
  2272. }
  2273. void Game::pauseAnimation()
  2274. {
  2275. pauseNodeAnimation(paused_animated_nodes, smgr->getRootSceneNode());
  2276. }
  2277. void Game::resumeAnimation()
  2278. {
  2279. for (auto &&pair: paused_animated_nodes)
  2280. pair.first->setAnimationSpeed(pair.second);
  2281. paused_animated_nodes.clear();
  2282. }
  2283. const ClientEventHandler Game::clientEventHandler[CLIENTEVENT_MAX] = {
  2284. {&Game::handleClientEvent_None},
  2285. {&Game::handleClientEvent_PlayerDamage},
  2286. {&Game::handleClientEvent_PlayerForceMove},
  2287. {&Game::handleClientEvent_Deathscreen},
  2288. {&Game::handleClientEvent_ShowFormSpec},
  2289. {&Game::handleClientEvent_ShowLocalFormSpec},
  2290. {&Game::handleClientEvent_HandleParticleEvent},
  2291. {&Game::handleClientEvent_HandleParticleEvent},
  2292. {&Game::handleClientEvent_HandleParticleEvent},
  2293. {&Game::handleClientEvent_HudAdd},
  2294. {&Game::handleClientEvent_HudRemove},
  2295. {&Game::handleClientEvent_HudChange},
  2296. {&Game::handleClientEvent_SetSky},
  2297. {&Game::handleClientEvent_SetSun},
  2298. {&Game::handleClientEvent_SetMoon},
  2299. {&Game::handleClientEvent_SetStars},
  2300. {&Game::handleClientEvent_OverrideDayNigthRatio},
  2301. {&Game::handleClientEvent_CloudParams},
  2302. };
  2303. void Game::handleClientEvent_None(ClientEvent *event, CameraOrientation *cam)
  2304. {
  2305. FATAL_ERROR("ClientEvent type None received");
  2306. }
  2307. void Game::handleClientEvent_PlayerDamage(ClientEvent *event, CameraOrientation *cam)
  2308. {
  2309. if (client->modsLoaded())
  2310. client->getScript()->on_damage_taken(event->player_damage.amount);
  2311. if (!event->player_damage.effect)
  2312. return;
  2313. // Damage flash and hurt tilt are not used at death
  2314. if (client->getHP() > 0) {
  2315. LocalPlayer *player = client->getEnv().getLocalPlayer();
  2316. f32 hp_max = player->getCAO() ?
  2317. player->getCAO()->getProperties().hp_max : PLAYER_MAX_HP_DEFAULT;
  2318. f32 damage_ratio = event->player_damage.amount / hp_max;
  2319. runData.damage_flash += 95.0f + 64.f * damage_ratio;
  2320. runData.damage_flash = MYMIN(runData.damage_flash, 127.0f);
  2321. player->hurt_tilt_timer = 1.5f;
  2322. player->hurt_tilt_strength =
  2323. rangelim(damage_ratio * 5.0f, 1.0f, 4.0f);
  2324. }
  2325. // Play damage sound
  2326. client->getEventManager()->put(new SimpleTriggerEvent(MtEvent::PLAYER_DAMAGE));
  2327. }
  2328. void Game::handleClientEvent_PlayerForceMove(ClientEvent *event, CameraOrientation *cam)
  2329. {
  2330. cam->camera_yaw = event->player_force_move.yaw;
  2331. cam->camera_pitch = event->player_force_move.pitch;
  2332. }
  2333. void Game::handleClientEvent_Deathscreen(ClientEvent *event, CameraOrientation *cam)
  2334. {
  2335. // If client scripting is enabled, deathscreen is handled by CSM code in
  2336. // builtin/client/init.lua
  2337. if (client->modsLoaded())
  2338. client->getScript()->on_death();
  2339. else
  2340. showDeathFormspec();
  2341. /* Handle visualization */
  2342. LocalPlayer *player = client->getEnv().getLocalPlayer();
  2343. runData.damage_flash = 0;
  2344. player->hurt_tilt_timer = 0;
  2345. player->hurt_tilt_strength = 0;
  2346. }
  2347. void Game::handleClientEvent_ShowFormSpec(ClientEvent *event, CameraOrientation *cam)
  2348. {
  2349. if (event->show_formspec.formspec->empty()) {
  2350. auto formspec = m_game_ui->getFormspecGUI();
  2351. if (formspec && (event->show_formspec.formname->empty()
  2352. || *(event->show_formspec.formname) == m_game_ui->getFormspecName())) {
  2353. formspec->quitMenu();
  2354. }
  2355. } else {
  2356. FormspecFormSource *fs_src =
  2357. new FormspecFormSource(*(event->show_formspec.formspec));
  2358. TextDestPlayerInventory *txt_dst =
  2359. new TextDestPlayerInventory(client, *(event->show_formspec.formname));
  2360. auto *&formspec = m_game_ui->updateFormspec(*(event->show_formspec.formname));
  2361. GUIFormSpecMenu::create(formspec, client, m_rendering_engine->get_gui_env(),
  2362. &input->joystick, fs_src, txt_dst, client->getFormspecPrepend(),
  2363. sound_manager.get());
  2364. }
  2365. delete event->show_formspec.formspec;
  2366. delete event->show_formspec.formname;
  2367. }
  2368. void Game::handleClientEvent_ShowLocalFormSpec(ClientEvent *event, CameraOrientation *cam)
  2369. {
  2370. FormspecFormSource *fs_src = new FormspecFormSource(*event->show_formspec.formspec);
  2371. LocalFormspecHandler *txt_dst =
  2372. new LocalFormspecHandler(*event->show_formspec.formname, client);
  2373. GUIFormSpecMenu::create(m_game_ui->getFormspecGUI(), client, m_rendering_engine->get_gui_env(),
  2374. &input->joystick, fs_src, txt_dst, client->getFormspecPrepend(), sound_manager.get());
  2375. delete event->show_formspec.formspec;
  2376. delete event->show_formspec.formname;
  2377. }
  2378. void Game::handleClientEvent_HandleParticleEvent(ClientEvent *event,
  2379. CameraOrientation *cam)
  2380. {
  2381. LocalPlayer *player = client->getEnv().getLocalPlayer();
  2382. client->getParticleManager()->handleParticleEvent(event, client, player);
  2383. }
  2384. void Game::handleClientEvent_HudAdd(ClientEvent *event, CameraOrientation *cam)
  2385. {
  2386. LocalPlayer *player = client->getEnv().getLocalPlayer();
  2387. u32 server_id = event->hudadd->server_id;
  2388. // ignore if we already have a HUD with that ID
  2389. auto i = m_hud_server_to_client.find(server_id);
  2390. if (i != m_hud_server_to_client.end()) {
  2391. delete event->hudadd;
  2392. return;
  2393. }
  2394. HudElement *e = new HudElement;
  2395. e->type = static_cast<HudElementType>(event->hudadd->type);
  2396. e->pos = event->hudadd->pos;
  2397. e->name = event->hudadd->name;
  2398. e->scale = event->hudadd->scale;
  2399. e->text = event->hudadd->text;
  2400. e->number = event->hudadd->number;
  2401. e->item = event->hudadd->item;
  2402. e->dir = event->hudadd->dir;
  2403. e->align = event->hudadd->align;
  2404. e->offset = event->hudadd->offset;
  2405. e->world_pos = event->hudadd->world_pos;
  2406. e->size = event->hudadd->size;
  2407. e->z_index = event->hudadd->z_index;
  2408. e->text2 = event->hudadd->text2;
  2409. e->style = event->hudadd->style;
  2410. m_hud_server_to_client[server_id] = player->addHud(e);
  2411. delete event->hudadd;
  2412. }
  2413. void Game::handleClientEvent_HudRemove(ClientEvent *event, CameraOrientation *cam)
  2414. {
  2415. LocalPlayer *player = client->getEnv().getLocalPlayer();
  2416. auto i = m_hud_server_to_client.find(event->hudrm.id);
  2417. if (i != m_hud_server_to_client.end()) {
  2418. HudElement *e = player->removeHud(i->second);
  2419. delete e;
  2420. m_hud_server_to_client.erase(i);
  2421. }
  2422. }
  2423. void Game::handleClientEvent_HudChange(ClientEvent *event, CameraOrientation *cam)
  2424. {
  2425. LocalPlayer *player = client->getEnv().getLocalPlayer();
  2426. HudElement *e = nullptr;
  2427. auto i = m_hud_server_to_client.find(event->hudchange->id);
  2428. if (i != m_hud_server_to_client.end()) {
  2429. e = player->getHud(i->second);
  2430. }
  2431. if (e == nullptr) {
  2432. delete event->hudchange;
  2433. return;
  2434. }
  2435. #define CASE_SET(statval, prop, dataprop) \
  2436. case statval: \
  2437. e->prop = event->hudchange->dataprop; \
  2438. break
  2439. switch (event->hudchange->stat) {
  2440. CASE_SET(HUD_STAT_POS, pos, v2fdata);
  2441. CASE_SET(HUD_STAT_NAME, name, sdata);
  2442. CASE_SET(HUD_STAT_SCALE, scale, v2fdata);
  2443. CASE_SET(HUD_STAT_TEXT, text, sdata);
  2444. CASE_SET(HUD_STAT_NUMBER, number, data);
  2445. CASE_SET(HUD_STAT_ITEM, item, data);
  2446. CASE_SET(HUD_STAT_DIR, dir, data);
  2447. CASE_SET(HUD_STAT_ALIGN, align, v2fdata);
  2448. CASE_SET(HUD_STAT_OFFSET, offset, v2fdata);
  2449. CASE_SET(HUD_STAT_WORLD_POS, world_pos, v3fdata);
  2450. CASE_SET(HUD_STAT_SIZE, size, v2s32data);
  2451. CASE_SET(HUD_STAT_Z_INDEX, z_index, data);
  2452. CASE_SET(HUD_STAT_TEXT2, text2, sdata);
  2453. CASE_SET(HUD_STAT_STYLE, style, data);
  2454. case HudElementStat_END:
  2455. break;
  2456. }
  2457. #undef CASE_SET
  2458. delete event->hudchange;
  2459. }
  2460. void Game::handleClientEvent_SetSky(ClientEvent *event, CameraOrientation *cam)
  2461. {
  2462. sky->setVisible(false);
  2463. // Whether clouds are visible in front of a custom skybox.
  2464. sky->setCloudsEnabled(event->set_sky->clouds);
  2465. // Clear the old textures out in case we switch rendering type.
  2466. sky->clearSkyboxTextures();
  2467. // Handle according to type
  2468. if (event->set_sky->type == "regular") {
  2469. // Shows the mesh skybox
  2470. sky->setVisible(true);
  2471. // Update mesh based skybox colours if applicable.
  2472. sky->setSkyColors(event->set_sky->sky_color);
  2473. sky->setHorizonTint(
  2474. event->set_sky->fog_sun_tint,
  2475. event->set_sky->fog_moon_tint,
  2476. event->set_sky->fog_tint_type
  2477. );
  2478. } else if (event->set_sky->type == "skybox" &&
  2479. event->set_sky->textures.size() == 6) {
  2480. // Disable the dyanmic mesh skybox:
  2481. sky->setVisible(false);
  2482. // Set fog colors:
  2483. sky->setFallbackBgColor(event->set_sky->bgcolor);
  2484. // Set sunrise and sunset fog tinting:
  2485. sky->setHorizonTint(
  2486. event->set_sky->fog_sun_tint,
  2487. event->set_sky->fog_moon_tint,
  2488. event->set_sky->fog_tint_type
  2489. );
  2490. // Add textures to skybox.
  2491. for (int i = 0; i < 6; i++)
  2492. sky->addTextureToSkybox(event->set_sky->textures[i], i, texture_src);
  2493. } else {
  2494. // Handle everything else as plain color.
  2495. if (event->set_sky->type != "plain")
  2496. infostream << "Unknown sky type: "
  2497. << (event->set_sky->type) << std::endl;
  2498. sky->setVisible(false);
  2499. sky->setFallbackBgColor(event->set_sky->bgcolor);
  2500. // Disable directional sun/moon tinting on plain or invalid skyboxes.
  2501. sky->setHorizonTint(
  2502. event->set_sky->bgcolor,
  2503. event->set_sky->bgcolor,
  2504. "custom"
  2505. );
  2506. }
  2507. // Orbit Tilt:
  2508. sky->setBodyOrbitTilt(event->set_sky->body_orbit_tilt);
  2509. // fog
  2510. // do not override a potentially smaller client setting.
  2511. sky->setFogDistance(event->set_sky->fog_distance);
  2512. // if the fog distance is reset, switch back to the client's viewing_range
  2513. if (event->set_sky->fog_distance < 0)
  2514. draw_control->wanted_range = g_settings->getS16("viewing_range");
  2515. if (event->set_sky->fog_start >= 0)
  2516. sky->setFogStart(rangelim(event->set_sky->fog_start, 0.0f, 0.99f));
  2517. else
  2518. sky->setFogStart(rangelim(g_settings->getFloat("fog_start"), 0.0f, 0.99f));
  2519. sky->setFogColor(event->set_sky->fog_color);
  2520. delete event->set_sky;
  2521. }
  2522. void Game::handleClientEvent_SetSun(ClientEvent *event, CameraOrientation *cam)
  2523. {
  2524. sky->setSunVisible(event->sun_params->visible);
  2525. sky->setSunTexture(event->sun_params->texture,
  2526. event->sun_params->tonemap, texture_src);
  2527. sky->setSunScale(event->sun_params->scale);
  2528. sky->setSunriseVisible(event->sun_params->sunrise_visible);
  2529. sky->setSunriseTexture(event->sun_params->sunrise, texture_src);
  2530. delete event->sun_params;
  2531. }
  2532. void Game::handleClientEvent_SetMoon(ClientEvent *event, CameraOrientation *cam)
  2533. {
  2534. sky->setMoonVisible(event->moon_params->visible);
  2535. sky->setMoonTexture(event->moon_params->texture,
  2536. event->moon_params->tonemap, texture_src);
  2537. sky->setMoonScale(event->moon_params->scale);
  2538. delete event->moon_params;
  2539. }
  2540. void Game::handleClientEvent_SetStars(ClientEvent *event, CameraOrientation *cam)
  2541. {
  2542. sky->setStarsVisible(event->star_params->visible);
  2543. sky->setStarCount(event->star_params->count);
  2544. sky->setStarColor(event->star_params->starcolor);
  2545. sky->setStarScale(event->star_params->scale);
  2546. sky->setStarDayOpacity(event->star_params->day_opacity);
  2547. delete event->star_params;
  2548. }
  2549. void Game::handleClientEvent_OverrideDayNigthRatio(ClientEvent *event,
  2550. CameraOrientation *cam)
  2551. {
  2552. client->getEnv().setDayNightRatioOverride(
  2553. event->override_day_night_ratio.do_override,
  2554. event->override_day_night_ratio.ratio_f * 1000.0f);
  2555. }
  2556. void Game::handleClientEvent_CloudParams(ClientEvent *event, CameraOrientation *cam)
  2557. {
  2558. if (!clouds)
  2559. return;
  2560. clouds->setDensity(event->cloud_params.density);
  2561. clouds->setColorBright(video::SColor(event->cloud_params.color_bright));
  2562. clouds->setColorAmbient(video::SColor(event->cloud_params.color_ambient));
  2563. clouds->setHeight(event->cloud_params.height);
  2564. clouds->setThickness(event->cloud_params.thickness);
  2565. clouds->setSpeed(v2f(event->cloud_params.speed_x, event->cloud_params.speed_y));
  2566. }
  2567. void Game::processClientEvents(CameraOrientation *cam)
  2568. {
  2569. while (client->hasClientEvents()) {
  2570. std::unique_ptr<ClientEvent> event(client->getClientEvent());
  2571. FATAL_ERROR_IF(event->type >= CLIENTEVENT_MAX, "Invalid clientevent type");
  2572. const ClientEventHandler& evHandler = clientEventHandler[event->type];
  2573. (this->*evHandler.handler)(event.get(), cam);
  2574. }
  2575. }
  2576. void Game::updateChat(f32 dtime)
  2577. {
  2578. // Get new messages from error log buffer
  2579. while (!m_chat_log_buf.empty())
  2580. chat_backend->addMessage(L"", utf8_to_wide(m_chat_log_buf.get()));
  2581. // Get new messages from client
  2582. std::wstring message;
  2583. while (client->getChatMessage(message)) {
  2584. chat_backend->addUnparsedMessage(message);
  2585. }
  2586. // Remove old messages
  2587. chat_backend->step(dtime);
  2588. // Display all messages in a static text element
  2589. auto &buf = chat_backend->getRecentBuffer();
  2590. if (buf.getLinesModified()) {
  2591. buf.resetLinesModified();
  2592. m_game_ui->setChatText(chat_backend->getRecentChat(), buf.getLineCount());
  2593. }
  2594. // Make sure that the size is still correct
  2595. m_game_ui->updateChatSize();
  2596. }
  2597. void Game::updateCamera(f32 dtime)
  2598. {
  2599. LocalPlayer *player = client->getEnv().getLocalPlayer();
  2600. /*
  2601. For interaction purposes, get info about the held item
  2602. - What item is it?
  2603. - Is it a usable item?
  2604. - Can it point to liquids?
  2605. */
  2606. ItemStack playeritem;
  2607. {
  2608. ItemStack selected, hand;
  2609. playeritem = player->getWieldedItem(&selected, &hand);
  2610. }
  2611. ToolCapabilities playeritem_toolcap =
  2612. playeritem.getToolCapabilities(itemdef_manager);
  2613. v3s16 old_camera_offset = camera->getOffset();
  2614. if (wasKeyPressed(KeyType::CAMERA_MODE)) {
  2615. GenericCAO *playercao = player->getCAO();
  2616. // If playercao not loaded, don't change camera
  2617. if (!playercao)
  2618. return;
  2619. camera->toggleCameraMode();
  2620. if (g_touchscreengui)
  2621. g_touchscreengui->setUseCrosshair(!isTouchCrosshairDisabled());
  2622. // Make the player visible depending on camera mode.
  2623. playercao->updateMeshCulling();
  2624. playercao->setChildrenVisible(camera->getCameraMode() > CAMERA_MODE_FIRST);
  2625. }
  2626. float full_punch_interval = playeritem_toolcap.full_punch_interval;
  2627. float tool_reload_ratio = runData.time_from_last_punch / full_punch_interval;
  2628. tool_reload_ratio = MYMIN(tool_reload_ratio, 1.0);
  2629. camera->update(player, dtime, tool_reload_ratio);
  2630. camera->step(dtime);
  2631. f32 camera_fov = camera->getFovMax();
  2632. v3s16 camera_offset = camera->getOffset();
  2633. m_camera_offset_changed = (camera_offset != old_camera_offset);
  2634. if (!m_flags.disable_camera_update) {
  2635. v3f camera_position = camera->getPosition();
  2636. v3f camera_direction = camera->getDirection();
  2637. client->getEnv().getClientMap().updateCamera(camera_position,
  2638. camera_direction, camera_fov, camera_offset, player->light_color);
  2639. if (m_camera_offset_changed) {
  2640. client->updateCameraOffset(camera_offset);
  2641. client->getEnv().updateCameraOffset(camera_offset);
  2642. if (clouds)
  2643. clouds->updateCameraOffset(camera_offset);
  2644. }
  2645. }
  2646. }
  2647. void Game::updateSound(f32 dtime)
  2648. {
  2649. // Update sound listener
  2650. LocalPlayer *player = client->getEnv().getLocalPlayer();
  2651. ClientActiveObject *parent = player->getParent();
  2652. v3s16 camera_offset = camera->getOffset();
  2653. sound_manager->updateListener(
  2654. (1.0f/BS) * camera->getCameraNode()->getPosition()
  2655. + intToFloat(camera_offset, 1.0f),
  2656. (1.0f/BS) * (parent ? parent->getVelocity() : player->getSpeed()),
  2657. camera->getDirection(),
  2658. camera->getCameraNode()->getUpVector());
  2659. sound_volume_control(sound_manager.get(), device->isWindowActive());
  2660. // Tell the sound maker whether to make footstep sounds
  2661. soundmaker->makes_footstep_sound = player->makes_footstep_sound;
  2662. // Update sound maker
  2663. if (player->makes_footstep_sound)
  2664. soundmaker->step(dtime);
  2665. ClientMap &map = client->getEnv().getClientMap();
  2666. MapNode n = map.getNode(player->getFootstepNodePos());
  2667. soundmaker->m_player_step_sound = nodedef_manager->get(n).sound_footstep;
  2668. }
  2669. void Game::processPlayerInteraction(f32 dtime, bool show_hud)
  2670. {
  2671. LocalPlayer *player = client->getEnv().getLocalPlayer();
  2672. const v3f camera_direction = camera->getDirection();
  2673. const v3s16 camera_offset = camera->getOffset();
  2674. /*
  2675. Calculate what block is the crosshair pointing to
  2676. */
  2677. ItemStack selected_item, hand_item;
  2678. const ItemStack &tool_item = player->getWieldedItem(&selected_item, &hand_item);
  2679. const ItemDefinition &selected_def = selected_item.getDefinition(itemdef_manager);
  2680. f32 d = getToolRange(selected_def, hand_item.getDefinition(itemdef_manager));
  2681. core::line3d<f32> shootline;
  2682. switch (camera->getCameraMode()) {
  2683. case CAMERA_MODE_FIRST:
  2684. // Shoot from camera position, with bobbing
  2685. shootline.start = camera->getPosition();
  2686. break;
  2687. case CAMERA_MODE_THIRD:
  2688. // Shoot from player head, no bobbing
  2689. shootline.start = camera->getHeadPosition();
  2690. break;
  2691. case CAMERA_MODE_THIRD_FRONT:
  2692. shootline.start = camera->getHeadPosition();
  2693. // prevent player pointing anything in front-view
  2694. d = 0;
  2695. break;
  2696. }
  2697. shootline.end = shootline.start + camera_direction * BS * d;
  2698. if (g_touchscreengui && isTouchCrosshairDisabled()) {
  2699. shootline = g_touchscreengui->getShootline();
  2700. // Scale shootline to the acual distance the player can reach
  2701. shootline.end = shootline.start +
  2702. shootline.getVector().normalize() * BS * d;
  2703. shootline.start += intToFloat(camera_offset, BS);
  2704. shootline.end += intToFloat(camera_offset, BS);
  2705. }
  2706. PointedThing pointed = updatePointedThing(shootline,
  2707. selected_def.liquids_pointable,
  2708. selected_def.pointabilities,
  2709. !runData.btn_down_for_dig,
  2710. camera_offset);
  2711. if (pointed != runData.pointed_old)
  2712. infostream << "Pointing at " << pointed.dump() << std::endl;
  2713. if (g_touchscreengui)
  2714. g_touchscreengui->applyContextControls(selected_def.touch_interaction.getMode(pointed));
  2715. // Note that updating the selection mesh every frame is not particularly efficient,
  2716. // but the halo rendering code is already inefficient so there's no point in optimizing it here
  2717. hud->updateSelectionMesh(camera_offset);
  2718. // Allow digging again if button is not pressed
  2719. if (runData.digging_blocked && !isKeyDown(KeyType::DIG))
  2720. runData.digging_blocked = false;
  2721. /*
  2722. Stop digging when
  2723. - releasing dig button
  2724. - pointing away from node
  2725. */
  2726. if (runData.digging) {
  2727. if (wasKeyReleased(KeyType::DIG)) {
  2728. infostream << "Dig button released (stopped digging)" << std::endl;
  2729. runData.digging = false;
  2730. } else if (pointed != runData.pointed_old) {
  2731. if (pointed.type == POINTEDTHING_NODE
  2732. && runData.pointed_old.type == POINTEDTHING_NODE
  2733. && pointed.node_undersurface
  2734. == runData.pointed_old.node_undersurface) {
  2735. // Still pointing to the same node, but a different face.
  2736. // Don't reset.
  2737. } else {
  2738. infostream << "Pointing away from node (stopped digging)" << std::endl;
  2739. runData.digging = false;
  2740. hud->updateSelectionMesh(camera_offset);
  2741. }
  2742. }
  2743. if (!runData.digging) {
  2744. client->interact(INTERACT_STOP_DIGGING, runData.pointed_old);
  2745. client->setCrack(-1, v3s16(0, 0, 0));
  2746. runData.dig_time = 0.0;
  2747. }
  2748. } else if (runData.dig_instantly && wasKeyReleased(KeyType::DIG)) {
  2749. // Remove e.g. torches faster when clicking instead of holding dig button
  2750. runData.nodig_delay_timer = 0;
  2751. runData.dig_instantly = false;
  2752. }
  2753. if (!runData.digging && runData.btn_down_for_dig && !isKeyDown(KeyType::DIG))
  2754. runData.btn_down_for_dig = false;
  2755. runData.punching = false;
  2756. soundmaker->m_player_leftpunch_sound = SoundSpec();
  2757. soundmaker->m_player_leftpunch_sound2 = pointed.type != POINTEDTHING_NOTHING ?
  2758. selected_def.sound_use : selected_def.sound_use_air;
  2759. // Prepare for repeating, unless we're not supposed to
  2760. if (isKeyDown(KeyType::PLACE) && !g_settings->getBool("safe_dig_and_place"))
  2761. runData.repeat_place_timer += dtime;
  2762. else
  2763. runData.repeat_place_timer = 0;
  2764. if (selected_def.usable && isKeyDown(KeyType::DIG)) {
  2765. if (wasKeyPressed(KeyType::DIG) && (!client->modsLoaded() ||
  2766. !client->getScript()->on_item_use(selected_item, pointed)))
  2767. client->interact(INTERACT_USE, pointed);
  2768. } else if (pointed.type == POINTEDTHING_NODE) {
  2769. handlePointingAtNode(pointed, selected_item, hand_item, dtime);
  2770. } else if (pointed.type == POINTEDTHING_OBJECT) {
  2771. v3f player_position = player->getPosition();
  2772. bool basic_debug_allowed = client->checkPrivilege("debug") || (player->hud_flags & HUD_FLAG_BASIC_DEBUG);
  2773. handlePointingAtObject(pointed, tool_item, player_position,
  2774. m_game_ui->m_flags.show_basic_debug && basic_debug_allowed);
  2775. } else if (isKeyDown(KeyType::DIG)) {
  2776. // When button is held down in air, show continuous animation
  2777. runData.punching = true;
  2778. // Run callback even though item is not usable
  2779. if (wasKeyPressed(KeyType::DIG) && client->modsLoaded())
  2780. client->getScript()->on_item_use(selected_item, pointed);
  2781. } else if (wasKeyPressed(KeyType::PLACE)) {
  2782. handlePointingAtNothing(selected_item);
  2783. }
  2784. runData.pointed_old = pointed;
  2785. if (runData.punching || wasKeyPressed(KeyType::DIG))
  2786. camera->setDigging(0); // dig animation
  2787. input->clearWasKeyPressed();
  2788. input->clearWasKeyReleased();
  2789. // Ensure DIG & PLACE are marked as handled
  2790. wasKeyDown(KeyType::DIG);
  2791. wasKeyDown(KeyType::PLACE);
  2792. input->joystick.clearWasKeyPressed(KeyType::DIG);
  2793. input->joystick.clearWasKeyPressed(KeyType::PLACE);
  2794. input->joystick.clearWasKeyReleased(KeyType::DIG);
  2795. input->joystick.clearWasKeyReleased(KeyType::PLACE);
  2796. }
  2797. PointedThing Game::updatePointedThing(
  2798. const core::line3d<f32> &shootline,
  2799. bool liquids_pointable,
  2800. const std::optional<Pointabilities> &pointabilities,
  2801. bool look_for_object,
  2802. const v3s16 &camera_offset)
  2803. {
  2804. std::vector<aabb3f> *selectionboxes = hud->getSelectionBoxes();
  2805. selectionboxes->clear();
  2806. hud->setSelectedFaceNormal(v3f());
  2807. static thread_local const bool show_entity_selectionbox = g_settings->getBool(
  2808. "show_entity_selectionbox");
  2809. ClientEnvironment &env = client->getEnv();
  2810. ClientMap &map = env.getClientMap();
  2811. const NodeDefManager *nodedef = map.getNodeDefManager();
  2812. runData.selected_object = NULL;
  2813. hud->pointing_at_object = false;
  2814. RaycastState s(shootline, look_for_object, liquids_pointable, pointabilities);
  2815. PointedThing result;
  2816. env.continueRaycast(&s, &result);
  2817. if (result.type == POINTEDTHING_OBJECT) {
  2818. hud->pointing_at_object = true;
  2819. runData.selected_object = client->getEnv().getActiveObject(result.object_id);
  2820. aabb3f selection_box;
  2821. if (show_entity_selectionbox && runData.selected_object->doShowSelectionBox() &&
  2822. runData.selected_object->getSelectionBox(&selection_box)) {
  2823. v3f pos = runData.selected_object->getPosition();
  2824. selectionboxes->push_back(aabb3f(selection_box));
  2825. hud->setSelectionPos(pos, camera_offset);
  2826. GenericCAO* gcao = dynamic_cast<GenericCAO*>(runData.selected_object);
  2827. if (gcao != nullptr && gcao->getProperties().rotate_selectionbox)
  2828. hud->setSelectionRotation(gcao->getSceneNode()->getAbsoluteTransformation().getRotationDegrees());
  2829. else
  2830. hud->setSelectionRotation(v3f());
  2831. }
  2832. hud->setSelectedFaceNormal(result.raw_intersection_normal);
  2833. } else if (result.type == POINTEDTHING_NODE) {
  2834. // Update selection boxes
  2835. MapNode n = map.getNode(result.node_undersurface);
  2836. std::vector<aabb3f> boxes;
  2837. n.getSelectionBoxes(nodedef, &boxes,
  2838. n.getNeighbors(result.node_undersurface, &map));
  2839. f32 d = 0.002 * BS;
  2840. for (std::vector<aabb3f>::const_iterator i = boxes.begin();
  2841. i != boxes.end(); ++i) {
  2842. aabb3f box = *i;
  2843. box.MinEdge -= v3f(d, d, d);
  2844. box.MaxEdge += v3f(d, d, d);
  2845. selectionboxes->push_back(box);
  2846. }
  2847. hud->setSelectionPos(intToFloat(result.node_undersurface, BS),
  2848. camera_offset);
  2849. hud->setSelectionRotation(v3f());
  2850. hud->setSelectedFaceNormal(result.intersection_normal);
  2851. }
  2852. // Update selection mesh light level and vertex colors
  2853. if (!selectionboxes->empty()) {
  2854. v3f pf = hud->getSelectionPos();
  2855. v3s16 p = floatToInt(pf, BS);
  2856. // Get selection mesh light level
  2857. MapNode n = map.getNode(p);
  2858. u16 node_light = getInteriorLight(n, -1, nodedef);
  2859. u16 light_level = node_light;
  2860. for (const v3s16 &dir : g_6dirs) {
  2861. n = map.getNode(p + dir);
  2862. node_light = getInteriorLight(n, -1, nodedef);
  2863. if (node_light > light_level)
  2864. light_level = node_light;
  2865. }
  2866. u32 daynight_ratio = client->getEnv().getDayNightRatio();
  2867. video::SColor c;
  2868. final_color_blend(&c, light_level, daynight_ratio);
  2869. // Modify final color a bit with time
  2870. u32 timer = client->getEnv().getFrameTime() % 5000;
  2871. float timerf = (float) (irr::core::PI * ((timer / 2500.0) - 0.5));
  2872. float sin_r = 0.08f * std::sin(timerf);
  2873. float sin_g = 0.08f * std::sin(timerf + irr::core::PI * 0.5f);
  2874. float sin_b = 0.08f * std::sin(timerf + irr::core::PI);
  2875. c.setRed(core::clamp(core::round32(c.getRed() * (0.8 + sin_r)), 0, 255));
  2876. c.setGreen(core::clamp(core::round32(c.getGreen() * (0.8 + sin_g)), 0, 255));
  2877. c.setBlue(core::clamp(core::round32(c.getBlue() * (0.8 + sin_b)), 0, 255));
  2878. // Set mesh final color
  2879. hud->setSelectionMeshColor(c);
  2880. }
  2881. return result;
  2882. }
  2883. void Game::handlePointingAtNothing(const ItemStack &playerItem)
  2884. {
  2885. infostream << "Attempted to place item while pointing at nothing" << std::endl;
  2886. PointedThing fauxPointed;
  2887. fauxPointed.type = POINTEDTHING_NOTHING;
  2888. client->interact(INTERACT_ACTIVATE, fauxPointed);
  2889. }
  2890. void Game::handlePointingAtNode(const PointedThing &pointed,
  2891. const ItemStack &selected_item, const ItemStack &hand_item, f32 dtime)
  2892. {
  2893. v3s16 nodepos = pointed.node_undersurface;
  2894. v3s16 neighborpos = pointed.node_abovesurface;
  2895. /*
  2896. Check information text of node
  2897. */
  2898. ClientMap &map = client->getEnv().getClientMap();
  2899. if (runData.nodig_delay_timer <= 0.0 && isKeyDown(KeyType::DIG)
  2900. && !runData.digging_blocked
  2901. && client->checkPrivilege("interact")) {
  2902. handleDigging(pointed, nodepos, selected_item, hand_item, dtime);
  2903. }
  2904. // This should be done after digging handling
  2905. NodeMetadata *meta = map.getNodeMetadata(nodepos);
  2906. if (meta) {
  2907. m_game_ui->setInfoText(unescape_translate(utf8_to_wide(
  2908. meta->getString("infotext"))));
  2909. } else {
  2910. MapNode n = map.getNode(nodepos);
  2911. if (nodedef_manager->get(n).name == "unknown") {
  2912. m_game_ui->setInfoText(L"Unknown node");
  2913. }
  2914. }
  2915. if ((wasKeyPressed(KeyType::PLACE) ||
  2916. runData.repeat_place_timer >= m_repeat_place_time) &&
  2917. client->checkPrivilege("interact")) {
  2918. runData.repeat_place_timer = 0;
  2919. infostream << "Place button pressed while looking at ground" << std::endl;
  2920. // Placing animation (always shown for feedback)
  2921. camera->setDigging(1);
  2922. soundmaker->m_player_rightpunch_sound = SoundSpec();
  2923. // If the wielded item has node placement prediction,
  2924. // make that happen
  2925. // And also set the sound and send the interact
  2926. // But first check for meta formspec and rightclickable
  2927. auto &def = selected_item.getDefinition(itemdef_manager);
  2928. bool placed = nodePlacement(def, selected_item, nodepos, neighborpos,
  2929. pointed, meta);
  2930. if (placed && client->modsLoaded())
  2931. client->getScript()->on_placenode(pointed, def);
  2932. }
  2933. }
  2934. bool Game::nodePlacement(const ItemDefinition &selected_def,
  2935. const ItemStack &selected_item, const v3s16 &nodepos, const v3s16 &neighborpos,
  2936. const PointedThing &pointed, const NodeMetadata *meta)
  2937. {
  2938. const auto &prediction = selected_def.node_placement_prediction;
  2939. const NodeDefManager *nodedef = client->ndef();
  2940. ClientMap &map = client->getEnv().getClientMap();
  2941. MapNode node;
  2942. bool is_valid_position;
  2943. node = map.getNode(nodepos, &is_valid_position);
  2944. if (!is_valid_position) {
  2945. soundmaker->m_player_rightpunch_sound = selected_def.sound_place_failed;
  2946. return false;
  2947. }
  2948. // formspec in meta
  2949. if (meta && !meta->getString("formspec").empty() && !input->isRandom()
  2950. && !isKeyDown(KeyType::SNEAK)) {
  2951. // on_rightclick callbacks are called anyway
  2952. if (nodedef_manager->get(map.getNode(nodepos)).rightclickable)
  2953. client->interact(INTERACT_PLACE, pointed);
  2954. infostream << "Launching custom inventory view" << std::endl;
  2955. InventoryLocation inventoryloc;
  2956. inventoryloc.setNodeMeta(nodepos);
  2957. NodeMetadataFormSource *fs_src = new NodeMetadataFormSource(
  2958. &client->getEnv().getClientMap(), nodepos);
  2959. TextDest *txt_dst = new TextDestNodeMetadata(nodepos, client);
  2960. auto *&formspec = m_game_ui->updateFormspec("");
  2961. GUIFormSpecMenu::create(formspec, client, m_rendering_engine->get_gui_env(),
  2962. &input->joystick, fs_src, txt_dst, client->getFormspecPrepend(),
  2963. sound_manager.get());
  2964. formspec->setFormSpec(meta->getString("formspec"), inventoryloc);
  2965. return false;
  2966. }
  2967. // on_rightclick callback
  2968. if (prediction.empty() || (nodedef->get(node).rightclickable &&
  2969. !isKeyDown(KeyType::SNEAK))) {
  2970. // Report to server
  2971. client->interact(INTERACT_PLACE, pointed);
  2972. return false;
  2973. }
  2974. verbosestream << "Node placement prediction for "
  2975. << selected_def.name << " is " << prediction << std::endl;
  2976. v3s16 p = neighborpos;
  2977. // Place inside node itself if buildable_to
  2978. MapNode n_under = map.getNode(nodepos, &is_valid_position);
  2979. if (is_valid_position) {
  2980. if (nodedef->get(n_under).buildable_to) {
  2981. p = nodepos;
  2982. } else {
  2983. node = map.getNode(p, &is_valid_position);
  2984. if (is_valid_position && !nodedef->get(node).buildable_to) {
  2985. soundmaker->m_player_rightpunch_sound = selected_def.sound_place_failed;
  2986. // Report to server
  2987. client->interact(INTERACT_PLACE, pointed);
  2988. return false;
  2989. }
  2990. }
  2991. }
  2992. // Find id of predicted node
  2993. content_t id;
  2994. bool found = nodedef->getId(prediction, id);
  2995. if (!found) {
  2996. errorstream << "Node placement prediction failed for "
  2997. << selected_def.name << " (places " << prediction
  2998. << ") - Name not known" << std::endl;
  2999. // Handle this as if prediction was empty
  3000. // Report to server
  3001. client->interact(INTERACT_PLACE, pointed);
  3002. return false;
  3003. }
  3004. const ContentFeatures &predicted_f = nodedef->get(id);
  3005. // Compare core.item_place_node() for what the server does with param2
  3006. MapNode predicted_node(id, 0, 0);
  3007. const auto place_param2 = selected_def.place_param2;
  3008. if (place_param2) {
  3009. predicted_node.setParam2(*place_param2);
  3010. } else if (predicted_f.param_type_2 == CPT2_WALLMOUNTED ||
  3011. predicted_f.param_type_2 == CPT2_COLORED_WALLMOUNTED) {
  3012. v3s16 dir = nodepos - neighborpos;
  3013. if (abs(dir.Y) > MYMAX(abs(dir.X), abs(dir.Z))) {
  3014. // If you change this code, also change builtin/game/item.lua
  3015. u8 predicted_param2 = dir.Y < 0 ? 1 : 0;
  3016. if (selected_def.wallmounted_rotate_vertical) {
  3017. bool rotate90 = false;
  3018. v3f fnodepos = v3f(neighborpos.X, neighborpos.Y, neighborpos.Z);
  3019. v3f ppos = client->getEnv().getLocalPlayer()->getPosition() / BS;
  3020. v3f pdir = fnodepos - ppos;
  3021. switch (predicted_f.drawtype) {
  3022. case NDT_TORCHLIKE: {
  3023. rotate90 = !((pdir.X < 0 && pdir.Z > 0) ||
  3024. (pdir.X > 0 && pdir.Z < 0));
  3025. if (dir.Y > 0) {
  3026. rotate90 = !rotate90;
  3027. }
  3028. break;
  3029. };
  3030. case NDT_SIGNLIKE: {
  3031. rotate90 = std::abs(pdir.X) < std::abs(pdir.Z);
  3032. break;
  3033. }
  3034. default: {
  3035. rotate90 = std::abs(pdir.X) > std::abs(pdir.Z);
  3036. break;
  3037. }
  3038. }
  3039. if (rotate90) {
  3040. predicted_param2 += 6;
  3041. }
  3042. }
  3043. predicted_node.setParam2(predicted_param2);
  3044. } else if (abs(dir.X) > abs(dir.Z)) {
  3045. predicted_node.setParam2(dir.X < 0 ? 3 : 2);
  3046. } else {
  3047. predicted_node.setParam2(dir.Z < 0 ? 5 : 4);
  3048. }
  3049. } else if (predicted_f.param_type_2 == CPT2_FACEDIR ||
  3050. predicted_f.param_type_2 == CPT2_COLORED_FACEDIR ||
  3051. predicted_f.param_type_2 == CPT2_4DIR ||
  3052. predicted_f.param_type_2 == CPT2_COLORED_4DIR) {
  3053. v3s16 dir = nodepos - floatToInt(client->getEnv().getLocalPlayer()->getPosition(), BS);
  3054. if (abs(dir.X) > abs(dir.Z)) {
  3055. predicted_node.setParam2(dir.X < 0 ? 3 : 1);
  3056. } else {
  3057. predicted_node.setParam2(dir.Z < 0 ? 2 : 0);
  3058. }
  3059. }
  3060. // Check attachment if node is in group attached_node
  3061. int an = itemgroup_get(predicted_f.groups, "attached_node");
  3062. if (an != 0) {
  3063. v3s16 pp;
  3064. if (an == 3) {
  3065. pp = p + v3s16(0, -1, 0);
  3066. } else if (an == 4) {
  3067. pp = p + v3s16(0, 1, 0);
  3068. } else if (an == 2) {
  3069. if (predicted_f.param_type_2 == CPT2_FACEDIR ||
  3070. predicted_f.param_type_2 == CPT2_COLORED_FACEDIR ||
  3071. predicted_f.param_type_2 == CPT2_4DIR ||
  3072. predicted_f.param_type_2 == CPT2_COLORED_4DIR) {
  3073. pp = p + facedir_dirs[predicted_node.getFaceDir(nodedef)];
  3074. } else {
  3075. pp = p;
  3076. }
  3077. } else if (predicted_f.param_type_2 == CPT2_WALLMOUNTED ||
  3078. predicted_f.param_type_2 == CPT2_COLORED_WALLMOUNTED) {
  3079. pp = p + predicted_node.getWallMountedDir(nodedef);
  3080. } else {
  3081. pp = p + v3s16(0, -1, 0);
  3082. }
  3083. if (!nodedef->get(map.getNode(pp)).walkable) {
  3084. soundmaker->m_player_rightpunch_sound = selected_def.sound_place_failed;
  3085. // Report to server
  3086. client->interact(INTERACT_PLACE, pointed);
  3087. return false;
  3088. }
  3089. }
  3090. // Apply color
  3091. if (!place_param2 && (predicted_f.param_type_2 == CPT2_COLOR
  3092. || predicted_f.param_type_2 == CPT2_COLORED_FACEDIR
  3093. || predicted_f.param_type_2 == CPT2_COLORED_4DIR
  3094. || predicted_f.param_type_2 == CPT2_COLORED_WALLMOUNTED)) {
  3095. const auto &indexstr = selected_item.metadata.
  3096. getString("palette_index", 0);
  3097. if (!indexstr.empty()) {
  3098. s32 index = mystoi(indexstr);
  3099. if (predicted_f.param_type_2 == CPT2_COLOR) {
  3100. predicted_node.setParam2(index);
  3101. } else if (predicted_f.param_type_2 == CPT2_COLORED_WALLMOUNTED) {
  3102. // param2 = pure palette index + other
  3103. predicted_node.setParam2((index & 0xf8) | (predicted_node.getParam2() & 0x07));
  3104. } else if (predicted_f.param_type_2 == CPT2_COLORED_FACEDIR) {
  3105. // param2 = pure palette index + other
  3106. predicted_node.setParam2((index & 0xe0) | (predicted_node.getParam2() & 0x1f));
  3107. } else if (predicted_f.param_type_2 == CPT2_COLORED_4DIR) {
  3108. // param2 = pure palette index + other
  3109. predicted_node.setParam2((index & 0xfc) | (predicted_node.getParam2() & 0x03));
  3110. }
  3111. }
  3112. }
  3113. // Add node to client map
  3114. try {
  3115. LocalPlayer *player = client->getEnv().getLocalPlayer();
  3116. // Don't place node when player would be inside new node
  3117. // NOTE: This is to be eventually implemented by a mod as client-side Lua
  3118. if (!predicted_f.walkable ||
  3119. g_settings->getBool("enable_build_where_you_stand") ||
  3120. (client->checkPrivilege("noclip") && g_settings->getBool("noclip")) ||
  3121. (predicted_f.walkable &&
  3122. neighborpos != player->getStandingNodePos() + v3s16(0, 1, 0) &&
  3123. neighborpos != player->getStandingNodePos() + v3s16(0, 2, 0))) {
  3124. // This triggers the required mesh update too
  3125. client->addNode(p, predicted_node);
  3126. // Report to server
  3127. client->interact(INTERACT_PLACE, pointed);
  3128. // A node is predicted, also play a sound
  3129. soundmaker->m_player_rightpunch_sound = selected_def.sound_place;
  3130. return true;
  3131. } else {
  3132. soundmaker->m_player_rightpunch_sound = selected_def.sound_place_failed;
  3133. return false;
  3134. }
  3135. } catch (const InvalidPositionException &e) {
  3136. errorstream << "Node placement prediction failed for "
  3137. << selected_def.name << " (places "
  3138. << prediction << ") - Position not loaded" << std::endl;
  3139. soundmaker->m_player_rightpunch_sound = selected_def.sound_place_failed;
  3140. return false;
  3141. }
  3142. }
  3143. void Game::handlePointingAtObject(const PointedThing &pointed,
  3144. const ItemStack &tool_item, const v3f &player_position, bool show_debug)
  3145. {
  3146. std::wstring infotext = unescape_translate(
  3147. utf8_to_wide(runData.selected_object->infoText()));
  3148. if (show_debug) {
  3149. if (!infotext.empty()) {
  3150. infotext += L"\n";
  3151. }
  3152. infotext += utf8_to_wide(runData.selected_object->debugInfoText());
  3153. }
  3154. m_game_ui->setInfoText(infotext);
  3155. if (isKeyDown(KeyType::DIG)) {
  3156. bool do_punch = false;
  3157. bool do_punch_damage = false;
  3158. if (runData.object_hit_delay_timer <= 0.0) {
  3159. do_punch = true;
  3160. do_punch_damage = true;
  3161. runData.object_hit_delay_timer = object_hit_delay;
  3162. }
  3163. if (wasKeyPressed(KeyType::DIG))
  3164. do_punch = true;
  3165. if (do_punch) {
  3166. infostream << "Punched object" << std::endl;
  3167. runData.punching = true;
  3168. }
  3169. if (do_punch_damage) {
  3170. // Report direct punch
  3171. v3f objpos = runData.selected_object->getPosition();
  3172. v3f dir = (objpos - player_position).normalize();
  3173. bool disable_send = runData.selected_object->directReportPunch(
  3174. dir, &tool_item, runData.time_from_last_punch);
  3175. runData.time_from_last_punch = 0;
  3176. if (!disable_send)
  3177. client->interact(INTERACT_START_DIGGING, pointed);
  3178. }
  3179. } else if (wasKeyDown(KeyType::PLACE)) {
  3180. infostream << "Pressed place button while pointing at object" << std::endl;
  3181. client->interact(INTERACT_PLACE, pointed); // place
  3182. }
  3183. }
  3184. void Game::handleDigging(const PointedThing &pointed, const v3s16 &nodepos,
  3185. const ItemStack &selected_item, const ItemStack &hand_item, f32 dtime)
  3186. {
  3187. // See also: serverpackethandle.cpp, action == 2
  3188. LocalPlayer *player = client->getEnv().getLocalPlayer();
  3189. ClientMap &map = client->getEnv().getClientMap();
  3190. MapNode n = map.getNode(nodepos);
  3191. const auto &features = nodedef_manager->get(n);
  3192. // NOTE: Similar piece of code exists on the server side for
  3193. // cheat detection.
  3194. // Get digging parameters
  3195. DigParams params = getDigParams(features.groups,
  3196. &selected_item.getToolCapabilities(itemdef_manager),
  3197. selected_item.wear);
  3198. // If can't dig, try hand
  3199. if (!params.diggable) {
  3200. params = getDigParams(features.groups,
  3201. &hand_item.getToolCapabilities(itemdef_manager));
  3202. }
  3203. if (!params.diggable) {
  3204. // I guess nobody will wait for this long
  3205. runData.dig_time_complete = 10000000.0;
  3206. } else {
  3207. runData.dig_time_complete = params.time;
  3208. if (m_cache_enable_particles) {
  3209. client->getParticleManager()->addNodeParticle(client,
  3210. player, nodepos, n, features);
  3211. }
  3212. }
  3213. if (!runData.digging) {
  3214. infostream << "Started digging" << std::endl;
  3215. runData.dig_instantly = runData.dig_time_complete == 0;
  3216. if (client->modsLoaded() && client->getScript()->on_punchnode(nodepos, n))
  3217. return;
  3218. client->interact(INTERACT_START_DIGGING, pointed);
  3219. runData.digging = true;
  3220. runData.btn_down_for_dig = true;
  3221. }
  3222. if (!runData.dig_instantly) {
  3223. runData.dig_index = (float)crack_animation_length
  3224. * runData.dig_time
  3225. / runData.dig_time_complete;
  3226. } else {
  3227. // This is for e.g. torches
  3228. runData.dig_index = crack_animation_length;
  3229. }
  3230. const auto &sound_dig = features.sound_dig;
  3231. if (sound_dig.exists() && params.diggable) {
  3232. if (sound_dig.name == "__group") {
  3233. if (!params.main_group.empty()) {
  3234. soundmaker->m_player_leftpunch_sound.gain = 0.5;
  3235. soundmaker->m_player_leftpunch_sound.name =
  3236. std::string("default_dig_") +
  3237. params.main_group;
  3238. }
  3239. } else {
  3240. soundmaker->m_player_leftpunch_sound = sound_dig;
  3241. }
  3242. }
  3243. // Don't show cracks if not diggable
  3244. if (runData.dig_time_complete >= 100000.0) {
  3245. } else if (runData.dig_index < crack_animation_length) {
  3246. client->setCrack(runData.dig_index, nodepos);
  3247. } else {
  3248. infostream << "Digging completed" << std::endl;
  3249. client->setCrack(-1, v3s16(0, 0, 0));
  3250. runData.dig_time = 0;
  3251. runData.digging = false;
  3252. // we successfully dug, now block it from repeating if we want to be safe
  3253. if (g_settings->getBool("safe_dig_and_place"))
  3254. runData.digging_blocked = true;
  3255. runData.nodig_delay_timer =
  3256. runData.dig_time_complete / (float)crack_animation_length;
  3257. // We don't want a corresponding delay to very time consuming nodes
  3258. // and nodes without digging time (e.g. torches) get a fixed delay.
  3259. if (runData.nodig_delay_timer > 0.3)
  3260. runData.nodig_delay_timer = 0.3;
  3261. else if (runData.dig_instantly)
  3262. runData.nodig_delay_timer = 0.15;
  3263. if (client->modsLoaded() &&
  3264. client->getScript()->on_dignode(nodepos, n)) {
  3265. return;
  3266. }
  3267. if (features.node_dig_prediction == "air") {
  3268. client->removeNode(nodepos);
  3269. } else if (!features.node_dig_prediction.empty()) {
  3270. content_t id;
  3271. bool found = nodedef_manager->getId(features.node_dig_prediction, id);
  3272. if (found)
  3273. client->addNode(nodepos, id, true);
  3274. }
  3275. // implicit else: no prediction
  3276. client->interact(INTERACT_DIGGING_COMPLETED, pointed);
  3277. if (m_cache_enable_particles) {
  3278. client->getParticleManager()->addDiggingParticles(client,
  3279. player, nodepos, n, features);
  3280. }
  3281. // Send event to trigger sound
  3282. client->getEventManager()->put(new NodeDugEvent(nodepos, n));
  3283. }
  3284. if (runData.dig_time_complete < 100000.0) {
  3285. runData.dig_time += dtime;
  3286. } else {
  3287. runData.dig_time = 0;
  3288. client->setCrack(-1, nodepos);
  3289. }
  3290. camera->setDigging(0); // Dig animation
  3291. }
  3292. void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime,
  3293. const CameraOrientation &cam)
  3294. {
  3295. TimeTaker tt_update("Game::updateFrame()");
  3296. LocalPlayer *player = client->getEnv().getLocalPlayer();
  3297. /*
  3298. Frame time
  3299. */
  3300. client->getEnv().updateFrameTime(m_is_paused);
  3301. /*
  3302. Fog range
  3303. */
  3304. if (sky->getFogDistance() >= 0) {
  3305. draw_control->wanted_range = MYMIN(draw_control->wanted_range, sky->getFogDistance());
  3306. }
  3307. if (draw_control->range_all && sky->getFogDistance() < 0) {
  3308. runData.fog_range = FOG_RANGE_ALL;
  3309. } else {
  3310. runData.fog_range = draw_control->wanted_range * BS;
  3311. }
  3312. /*
  3313. Calculate general brightness
  3314. */
  3315. u32 daynight_ratio = client->getEnv().getDayNightRatio();
  3316. float time_brightness = decode_light_f((float)daynight_ratio / 1000.0);
  3317. float direct_brightness;
  3318. bool sunlight_seen;
  3319. // When in noclip mode force same sky brightness as above ground so you
  3320. // can see properly
  3321. if (draw_control->allow_noclip && m_cache_enable_free_move &&
  3322. client->checkPrivilege("fly")) {
  3323. direct_brightness = time_brightness;
  3324. sunlight_seen = true;
  3325. } else {
  3326. float old_brightness = sky->getBrightness();
  3327. direct_brightness = client->getEnv().getClientMap()
  3328. .getBackgroundBrightness(MYMIN(runData.fog_range * 1.2, 60 * BS),
  3329. daynight_ratio, (int)(old_brightness * 255.5), &sunlight_seen)
  3330. / 255.0;
  3331. }
  3332. float time_of_day_smooth = runData.time_of_day_smooth;
  3333. float time_of_day = client->getEnv().getTimeOfDayF();
  3334. static const float maxsm = 0.05f;
  3335. static const float todsm = 0.05f;
  3336. if (std::fabs(time_of_day - time_of_day_smooth) > maxsm &&
  3337. std::fabs(time_of_day - time_of_day_smooth + 1.0) > maxsm &&
  3338. std::fabs(time_of_day - time_of_day_smooth - 1.0) > maxsm)
  3339. time_of_day_smooth = time_of_day;
  3340. if (time_of_day_smooth > 0.8 && time_of_day < 0.2)
  3341. time_of_day_smooth = time_of_day_smooth * (1.0 - todsm)
  3342. + (time_of_day + 1.0) * todsm;
  3343. else
  3344. time_of_day_smooth = time_of_day_smooth * (1.0 - todsm)
  3345. + time_of_day * todsm;
  3346. runData.time_of_day_smooth = time_of_day_smooth;
  3347. sky->update(time_of_day_smooth, time_brightness, direct_brightness,
  3348. sunlight_seen, camera->getCameraMode(), player->getYaw(),
  3349. player->getPitch());
  3350. /*
  3351. Update clouds
  3352. */
  3353. if (clouds)
  3354. updateClouds(dtime);
  3355. /*
  3356. Update particles
  3357. */
  3358. client->getParticleManager()->step(dtime);
  3359. /*
  3360. Damage camera tilt
  3361. */
  3362. if (player->hurt_tilt_timer > 0.0f) {
  3363. player->hurt_tilt_timer -= dtime * 6.0f;
  3364. if (player->hurt_tilt_timer < 0.0f)
  3365. player->hurt_tilt_strength = 0.0f;
  3366. }
  3367. /*
  3368. Update minimap pos and rotation
  3369. */
  3370. if (mapper && m_game_ui->m_flags.show_hud) {
  3371. mapper->setPos(floatToInt(player->getPosition(), BS));
  3372. mapper->setAngle(player->getYaw());
  3373. }
  3374. /*
  3375. Get chat messages from client
  3376. */
  3377. updateChat(dtime);
  3378. /*
  3379. Inventory
  3380. */
  3381. if (player->getWieldIndex() != runData.new_playeritem)
  3382. client->setPlayerItem(runData.new_playeritem);
  3383. if (client->updateWieldedItem()) {
  3384. // Update wielded tool
  3385. ItemStack selected_item, hand_item;
  3386. ItemStack &tool_item = player->getWieldedItem(&selected_item, &hand_item);
  3387. camera->wield(tool_item);
  3388. }
  3389. /*
  3390. Update block draw list every 200ms or when camera direction has
  3391. changed much
  3392. */
  3393. runData.update_draw_list_timer += dtime;
  3394. runData.touch_blocks_timer += dtime;
  3395. float update_draw_list_delta = 0.2f;
  3396. v3f camera_direction = camera->getDirection();
  3397. // call only one of updateDrawList, touchMapBlocks, or updateShadow per frame
  3398. // (the else-ifs below are intentional)
  3399. if (runData.update_draw_list_timer >= update_draw_list_delta
  3400. || runData.update_draw_list_last_cam_dir.getDistanceFrom(camera_direction) > 0.2
  3401. || m_camera_offset_changed
  3402. || client->getEnv().getClientMap().needsUpdateDrawList()) {
  3403. runData.update_draw_list_timer = 0;
  3404. client->getEnv().getClientMap().updateDrawList();
  3405. runData.update_draw_list_last_cam_dir = camera_direction;
  3406. } else if (runData.touch_blocks_timer > update_draw_list_delta) {
  3407. client->getEnv().getClientMap().touchMapBlocks();
  3408. runData.touch_blocks_timer = 0;
  3409. } else if (RenderingEngine::get_shadow_renderer()) {
  3410. updateShadows();
  3411. }
  3412. m_game_ui->update(*stats, client, draw_control, cam, runData.pointed_old, gui_chat_console, dtime);
  3413. /*
  3414. make sure menu is on top
  3415. 1. Delete formspec menu reference if menu was removed
  3416. 2. Else, make sure formspec menu is on top
  3417. */
  3418. auto formspec = m_game_ui->getFormspecGUI();
  3419. do { // breakable. only runs for one iteration
  3420. if (!formspec)
  3421. break;
  3422. if (formspec->getReferenceCount() == 1) {
  3423. // See GUIFormSpecMenu::create what refcnt = 1 means
  3424. m_game_ui->deleteFormspec();
  3425. break;
  3426. }
  3427. auto &loc = formspec->getFormspecLocation();
  3428. if (loc.type == InventoryLocation::NODEMETA) {
  3429. NodeMetadata *meta = client->getEnv().getClientMap().getNodeMetadata(loc.p);
  3430. if (!meta || meta->getString("formspec").empty()) {
  3431. formspec->quitMenu();
  3432. break;
  3433. }
  3434. }
  3435. if (isMenuActive())
  3436. guiroot->bringToFront(formspec);
  3437. } while (false);
  3438. /*
  3439. ==================== Drawing begins ====================
  3440. */
  3441. if (device->isWindowVisible())
  3442. drawScene(graph, stats);
  3443. /*
  3444. ==================== End scene ====================
  3445. */
  3446. // Damage flash is drawn in drawScene, but the timing update is done here to
  3447. // keep dtime out of the drawing code.
  3448. if (runData.damage_flash > 0.0f) {
  3449. runData.damage_flash -= 384.0f * dtime;
  3450. }
  3451. g_profiler->avg("Game::updateFrame(): update frame [ms]", tt_update.stop(true));
  3452. }
  3453. void Game::updateClouds(float dtime)
  3454. {
  3455. if (this->sky->getCloudsVisible()) {
  3456. this->clouds->setVisible(true);
  3457. this->clouds->step(dtime);
  3458. // this->camera->getPosition is not enough for third-person camera.
  3459. v3f camera_node_position = this->camera->getCameraNode()->getPosition();
  3460. v3s16 camera_offset = this->camera->getOffset();
  3461. camera_node_position.X = camera_node_position.X + camera_offset.X * BS;
  3462. camera_node_position.Y = camera_node_position.Y + camera_offset.Y * BS;
  3463. camera_node_position.Z = camera_node_position.Z + camera_offset.Z * BS;
  3464. this->clouds->update(camera_node_position, this->sky->getCloudColor());
  3465. if (this->clouds->isCameraInsideCloud() && this->m_cache_enable_fog) {
  3466. // If camera is inside cloud and fog is enabled, use cloud's colors as sky colors.
  3467. video::SColor clouds_dark = this->clouds->getColor().getInterpolated(
  3468. video::SColor(255, 0, 0, 0), 0.9);
  3469. this->sky->overrideColors(clouds_dark, this->clouds->getColor());
  3470. this->sky->setInClouds(true);
  3471. this->runData.fog_range = std::fmin(this->runData.fog_range * 0.5f, 32.0f * BS);
  3472. // Clouds are not drawn in this case.
  3473. this->clouds->setVisible(false);
  3474. }
  3475. } else {
  3476. this->clouds->setVisible(false);
  3477. }
  3478. }
  3479. /* Log times and stuff for visualization */
  3480. inline void Game::updateProfilerGraphs(ProfilerGraph *graph)
  3481. {
  3482. Profiler::GraphValues values;
  3483. g_profiler->graphGet(values);
  3484. graph->put(values);
  3485. }
  3486. /****************************************************************************
  3487. * Shadows
  3488. *****************************************************************************/
  3489. void Game::updateShadows()
  3490. {
  3491. ShadowRenderer *shadow = RenderingEngine::get_shadow_renderer();
  3492. if (!shadow)
  3493. return;
  3494. float in_timeofday = std::fmod(runData.time_of_day_smooth, 1.0f);
  3495. float timeoftheday = getWickedTimeOfDay(in_timeofday);
  3496. bool is_day = timeoftheday > 0.25 && timeoftheday < 0.75;
  3497. bool is_shadow_visible = is_day ? sky->getSunVisible() : sky->getMoonVisible();
  3498. shadow->setShadowIntensity(is_shadow_visible ? client->getEnv().getLocalPlayer()->getLighting().shadow_intensity : 0.0f);
  3499. timeoftheday = std::fmod(timeoftheday + 0.75f, 0.5f) + 0.25f;
  3500. const float offset_constant = 10000.0f;
  3501. v3f light = is_day ? sky->getSunDirection() : sky->getMoonDirection();
  3502. v3f sun_pos = light * offset_constant;
  3503. shadow->getDirectionalLight().setDirection(sun_pos);
  3504. shadow->setTimeOfDay(in_timeofday);
  3505. shadow->getDirectionalLight().update_frustum(camera, client, m_camera_offset_changed);
  3506. }
  3507. void Game::drawScene(ProfilerGraph *graph, RunStats *stats)
  3508. {
  3509. const video::SColor fog_color = this->sky->getFogColor();
  3510. const video::SColor sky_color = this->sky->getSkyColor();
  3511. /*
  3512. Fog
  3513. */
  3514. if (this->m_cache_enable_fog) {
  3515. this->driver->setFog(
  3516. fog_color,
  3517. video::EFT_FOG_LINEAR,
  3518. this->runData.fog_range * this->sky->getFogStart(),
  3519. this->runData.fog_range * 1.0f,
  3520. 0.f, // unused
  3521. false, // pixel fog
  3522. true // range fog
  3523. );
  3524. } else {
  3525. this->driver->setFog(
  3526. fog_color,
  3527. video::EFT_FOG_LINEAR,
  3528. FOG_RANGE_ALL,
  3529. FOG_RANGE_ALL + 100 * BS,
  3530. 0.f, // unused
  3531. false, // pixel fog
  3532. false // range fog
  3533. );
  3534. }
  3535. /*
  3536. Drawing
  3537. */
  3538. TimeTaker tt_draw("Draw scene", nullptr, PRECISION_MICRO);
  3539. this->driver->beginScene(true, true, sky_color);
  3540. const LocalPlayer *player = this->client->getEnv().getLocalPlayer();
  3541. bool draw_wield_tool = (this->m_game_ui->m_flags.show_hud &&
  3542. (player->hud_flags & HUD_FLAG_WIELDITEM_VISIBLE) &&
  3543. (this->camera->getCameraMode() == CAMERA_MODE_FIRST));
  3544. bool draw_crosshair = (
  3545. (player->hud_flags & HUD_FLAG_CROSSHAIR_VISIBLE) &&
  3546. (this->camera->getCameraMode() != CAMERA_MODE_THIRD_FRONT));
  3547. if (g_touchscreengui && isTouchCrosshairDisabled())
  3548. draw_crosshair = false;
  3549. this->m_rendering_engine->draw_scene(sky_color, this->m_game_ui->m_flags.show_hud,
  3550. draw_wield_tool, draw_crosshair);
  3551. /*
  3552. Profiler graph
  3553. */
  3554. v2u32 screensize = this->driver->getScreenSize();
  3555. if (this->m_game_ui->m_flags.show_profiler_graph)
  3556. graph->draw(10, screensize.Y - 10, driver, g_fontengine->getFont());
  3557. /*
  3558. Damage flash
  3559. */
  3560. if (this->runData.damage_flash > 0.0f) {
  3561. video::SColor color(this->runData.damage_flash, 180, 0, 0);
  3562. this->driver->draw2DRectangle(color,
  3563. core::rect<s32>(0, 0, screensize.X, screensize.Y),
  3564. NULL);
  3565. }
  3566. this->driver->endScene();
  3567. stats->drawtime = tt_draw.stop(true);
  3568. g_profiler->graphAdd("Draw scene [us]", stats->drawtime);
  3569. }
  3570. /****************************************************************************
  3571. Misc
  3572. ****************************************************************************/
  3573. void Game::showOverlayMessage(const char *msg, float dtime, int percent, bool draw_sky)
  3574. {
  3575. m_rendering_engine->draw_load_screen(wstrgettext(msg), guienv, texture_src,
  3576. dtime, percent, draw_sky);
  3577. }
  3578. void Game::settingChangedCallback(const std::string &setting_name, void *data)
  3579. {
  3580. ((Game *)data)->readSettings();
  3581. }
  3582. void Game::readSettings()
  3583. {
  3584. m_cache_doubletap_jump = g_settings->getBool("doubletap_jump");
  3585. m_cache_enable_clouds = g_settings->getBool("enable_clouds");
  3586. m_cache_enable_joysticks = g_settings->getBool("enable_joysticks");
  3587. m_cache_enable_particles = g_settings->getBool("enable_particles");
  3588. m_cache_enable_fog = g_settings->getBool("enable_fog");
  3589. m_cache_mouse_sensitivity = g_settings->getFloat("mouse_sensitivity", 0.001f, 10.0f);
  3590. m_cache_joystick_frustum_sensitivity = std::max(g_settings->getFloat("joystick_frustum_sensitivity"), 0.001f);
  3591. m_repeat_place_time = g_settings->getFloat("repeat_place_time", 0.16f, 2.0);
  3592. m_cache_enable_noclip = g_settings->getBool("noclip");
  3593. m_cache_enable_free_move = g_settings->getBool("free_move");
  3594. m_cache_cam_smoothing = 0;
  3595. if (g_settings->getBool("cinematic"))
  3596. m_cache_cam_smoothing = 1 - g_settings->getFloat("cinematic_camera_smoothing");
  3597. else
  3598. m_cache_cam_smoothing = 1 - g_settings->getFloat("camera_smoothing");
  3599. m_cache_cam_smoothing = rangelim(m_cache_cam_smoothing, 0.01f, 1.0f);
  3600. m_cache_mouse_sensitivity = rangelim(m_cache_mouse_sensitivity, 0.001, 100.0);
  3601. m_invert_mouse = g_settings->getBool("invert_mouse");
  3602. m_enable_hotbar_mouse_wheel = g_settings->getBool("enable_hotbar_mouse_wheel");
  3603. m_invert_hotbar_mouse_wheel = g_settings->getBool("invert_hotbar_mouse_wheel");
  3604. m_does_lost_focus_pause_game = g_settings->getBool("pause_on_lost_focus");
  3605. }
  3606. /****************************************************************************/
  3607. /****************************************************************************
  3608. Shutdown / cleanup
  3609. ****************************************************************************/
  3610. /****************************************************************************/
  3611. void Game::showDeathFormspec()
  3612. {
  3613. static std::string formspec_str =
  3614. std::string("formspec_version[1]") +
  3615. SIZE_TAG
  3616. "bgcolor[#320000b4;true]"
  3617. "label[4.85,1.35;" + gettext("You died") + "]"
  3618. "button_exit[4,3;3,0.5;btn_respawn;" + gettext("Respawn") + "]"
  3619. ;
  3620. /* Create menu */
  3621. /* Note: FormspecFormSource and LocalFormspecHandler *
  3622. * are deleted by guiFormSpecMenu */
  3623. FormspecFormSource *fs_src = new FormspecFormSource(formspec_str);
  3624. LocalFormspecHandler *txt_dst = new LocalFormspecHandler("MT_DEATH_SCREEN", client);
  3625. auto *&formspec = m_game_ui->getFormspecGUI();
  3626. GUIFormSpecMenu::create(formspec, client, m_rendering_engine->get_gui_env(),
  3627. &input->joystick, fs_src, txt_dst, client->getFormspecPrepend(),
  3628. sound_manager.get());
  3629. formspec->setFocus("btn_respawn");
  3630. }
  3631. #define GET_KEY_NAME(KEY) gettext(getKeySetting(#KEY).name())
  3632. void Game::showPauseMenu()
  3633. {
  3634. std::string control_text;
  3635. if (g_touchscreengui) {
  3636. control_text = strgettext("Controls:\n"
  3637. "No menu open:\n"
  3638. "- slide finger: look around\n"
  3639. "- tap: place/punch/use (default)\n"
  3640. "- long tap: dig/use (default)\n"
  3641. "Menu/inventory open:\n"
  3642. "- double tap (outside):\n"
  3643. " --> close\n"
  3644. "- touch stack, touch slot:\n"
  3645. " --> move stack\n"
  3646. "- touch&drag, tap 2nd finger\n"
  3647. " --> place single item to slot\n"
  3648. );
  3649. }
  3650. float ypos = simple_singleplayer_mode ? 0.7f : 0.1f;
  3651. std::ostringstream os;
  3652. os << "formspec_version[1]" << SIZE_TAG
  3653. << "button_exit[4," << (ypos++) << ";3,0.5;btn_continue;"
  3654. << strgettext("Continue") << "]";
  3655. if (!simple_singleplayer_mode) {
  3656. os << "button_exit[4," << (ypos++) << ";3,0.5;btn_change_password;"
  3657. << strgettext("Change Password") << "]";
  3658. } else {
  3659. os << "field[4.95,0;5,1.5;;" << strgettext("Game paused") << ";]";
  3660. }
  3661. #ifndef __ANDROID__
  3662. #if USE_SOUND
  3663. if (g_settings->getBool("enable_sound")) {
  3664. os << "button_exit[4," << (ypos++) << ";3,0.5;btn_sound;"
  3665. << strgettext("Sound Volume") << "]";
  3666. }
  3667. #endif
  3668. os << "button_exit[4," << (ypos++) << ";3,0.5;btn_key_config;"
  3669. << strgettext("Controls") << "]";
  3670. #endif
  3671. os << "button_exit[4," << (ypos++) << ";3,0.5;btn_exit_menu;"
  3672. << strgettext("Exit to Menu") << "]";
  3673. os << "button_exit[4," << (ypos++) << ";3,0.5;btn_exit_os;"
  3674. << strgettext("Exit to OS") << "]";
  3675. if (!control_text.empty()) {
  3676. os << "textarea[7.5,0.25;3.9,6.25;;" << control_text << ";]";
  3677. }
  3678. os << "textarea[0.4,0.25;3.9,6.25;;" << PROJECT_NAME_C " " VERSION_STRING "\n"
  3679. << "\n"
  3680. << strgettext("Game info:") << "\n";
  3681. const std::string &address = client->getAddressName();
  3682. os << strgettext("- Mode: ");
  3683. if (!simple_singleplayer_mode) {
  3684. if (address.empty())
  3685. os << strgettext("Hosting server");
  3686. else
  3687. os << strgettext("Remote server");
  3688. } else {
  3689. os << strgettext("Singleplayer");
  3690. }
  3691. os << "\n";
  3692. if (simple_singleplayer_mode || address.empty()) {
  3693. static const std::string on = strgettext("On");
  3694. static const std::string off = strgettext("Off");
  3695. // Note: Status of enable_damage and creative_mode settings is intentionally
  3696. // NOT shown here because the game might roll its own damage system and/or do
  3697. // a per-player Creative Mode, in which case writing it here would mislead.
  3698. bool damage = g_settings->getBool("enable_damage");
  3699. const std::string &announced = g_settings->getBool("server_announce") ? on : off;
  3700. if (!simple_singleplayer_mode) {
  3701. if (damage) {
  3702. const std::string &pvp = g_settings->getBool("enable_pvp") ? on : off;
  3703. //~ PvP = Player versus Player
  3704. os << strgettext("- PvP: ") << pvp << "\n";
  3705. }
  3706. os << strgettext("- Public: ") << announced << "\n";
  3707. std::string server_name = g_settings->get("server_name");
  3708. str_formspec_escape(server_name);
  3709. if (announced == on && !server_name.empty())
  3710. os << strgettext("- Server Name: ") << server_name;
  3711. }
  3712. }
  3713. os << ";]";
  3714. /* Create menu */
  3715. /* Note: FormspecFormSource and LocalFormspecHandler *
  3716. * are deleted by guiFormSpecMenu */
  3717. FormspecFormSource *fs_src = new FormspecFormSource(os.str());
  3718. LocalFormspecHandler *txt_dst = new LocalFormspecHandler("MT_PAUSE_MENU");
  3719. auto *&formspec = m_game_ui->getFormspecGUI();
  3720. GUIFormSpecMenu::create(formspec, client, m_rendering_engine->get_gui_env(),
  3721. &input->joystick, fs_src, txt_dst, client->getFormspecPrepend(),
  3722. sound_manager.get());
  3723. formspec->setFocus("btn_continue");
  3724. // game will be paused in next step, if in singleplayer (see m_is_paused)
  3725. formspec->doPause = true;
  3726. }
  3727. /****************************************************************************/
  3728. /****************************************************************************
  3729. extern function for launching the game
  3730. ****************************************************************************/
  3731. /****************************************************************************/
  3732. void the_game(bool *kill,
  3733. InputHandler *input,
  3734. RenderingEngine *rendering_engine,
  3735. const GameStartData &start_data,
  3736. std::string &error_message,
  3737. ChatBackend &chat_backend,
  3738. bool *reconnect_requested) // Used for local game
  3739. {
  3740. Game game;
  3741. /* Make a copy of the server address because if a local singleplayer server
  3742. * is created then this is updated and we don't want to change the value
  3743. * passed to us by the calling function
  3744. */
  3745. try {
  3746. if (game.startup(kill, input, rendering_engine, start_data,
  3747. error_message, reconnect_requested, &chat_backend)) {
  3748. game.run();
  3749. }
  3750. } catch (SerializationError &e) {
  3751. const std::string ver_err = fmtgettext("The server is probably running a different version of %s.", PROJECT_NAME_C);
  3752. error_message = strgettext("A serialization error occurred:") +"\n"
  3753. + e.what() + "\n\n" + ver_err;
  3754. errorstream << error_message << std::endl;
  3755. } catch (ServerError &e) {
  3756. error_message = e.what();
  3757. errorstream << "ServerError: " << error_message << std::endl;
  3758. } catch (ModError &e) {
  3759. // DO NOT TRANSLATE the `ModError`, it's used by `ui.lua`
  3760. error_message = std::string("ModError: ") + e.what() +
  3761. strgettext("\nCheck debug.txt for details.");
  3762. errorstream << error_message << std::endl;
  3763. }
  3764. game.shutdown();
  3765. }