ServerMapSector* ServerMapSector::deSerialize( std::istream &is, Map *parent, v2s16 p2d, std::map<v2s16, MapSector*> & sectors, IGameDef *gamedef ) { /* [0] u8 serialization version + heightmap data */ /* Read stuff */ // Read version u8 version = SER_FMT_VER_INVALID; is.read((char*)&version, 1); if(!ser_ver_supported(version)) throw VersionMismatchException("ERROR: MapSector format not supported"); /* Add necessary reading stuff here */ /* Get or create sector */ ServerMapSector *sector = NULL; std::map<v2s16, MapSector*>::iterator n = sectors.find(p2d); if(n != sectors.end()) { dstream<<"WARNING: deSerializing existent sectors not supported " "at the moment, because code hasn't been tested." <<std::endl; MapSector *sector = n->second; assert(sector->getId() == MAPSECTOR_SERVER); return (ServerMapSector*)sector; } else { sector = new ServerMapSector(parent, p2d, gamedef); sectors[p2d] = sector; } /* Set stuff in sector */ // Nothing here return sector; }
virtual string problemDesc(unsigned index) { if (index >= sectors.size()) return "No unknown flats found"; MapSector* sector = sectors[index]; if (floor[index]) return S_FMT("Sector %d has unknown floor texture \"%s\"", sector->getIndex(), sector->getFloorTex()); else return S_FMT("Sector %d has unknown ceiling texture \"%s\"", sector->getIndex(), sector->getCeilingTex()); }
/* QuickTextureOverlay3d::applyTexture * Applies the current texture to all selected walls/flats *******************************************************************/ void QuickTextureOverlay3d::applyTexture() { // Check editor is associated if (!editor) return; // Get selection/hilight auto& selection = editor->selection(); // Go through items if (!selection.empty()) { for (unsigned a = 0; a < selection.size(); a++) { // Thing (skip) if (selection[a].type == MapEditor::ItemType::Thing) continue; // Floor else if (selection[a].type == MapEditor::ItemType::Floor && (sel_type == 0 || sel_type == 2)) { MapSector* sector = editor->map().getSector(selection[a].index); if (sector) sector->setStringProperty("texturefloor", textures[current_index].name); } // Ceiling else if (selection[a].type == MapEditor::ItemType::Ceiling && (sel_type == 0 || sel_type == 2)) { MapSector* sector = editor->map().getSector(selection[a].index); if (sector) sector->setStringProperty("textureceiling", textures[current_index].name); } // Wall else if (sel_type > 0) { MapSide* side = editor->map().getSide(selection[a].index); if (side) { // Upper if (selection[a].type == MapEditor::ItemType::WallTop) side->setStringProperty("texturetop", textures[current_index].name); // Middle else if (selection[a].type == MapEditor::ItemType::WallMiddle) side->setStringProperty("texturemiddle", textures[current_index].name); // Lower else if (selection[a].type == MapEditor::ItemType::WallBottom) side->setStringProperty("texturebottom", textures[current_index].name); } } } } }
string fixText(unsigned fix_type, unsigned index) { if (fix_type == 0) { if (index >= invalid_refs.size()) return "Fix Sector reference"; MapSector* sector = invalid_refs[index].sector; if (sector) return S_FMT("Set to Sector #%d", sector->getIndex()); else return S_FMT("Clear Sector"); } return ""; }
void MapSpecials::applyVavoomSlopeThing(SLADEMap* map, MapThing* thing) { int target_idx = map->sectorAt(thing->point()); if (target_idx < 0) return; MapSector* target = map->getSector(target_idx); int tid = thing->intProperty("id"); vector<MapLine*> lines; target->getLines(lines); // TODO unclear if this is the same order that ZDoom would go through the // lines, which matters if two lines have the same first arg for (unsigned a = 0; a < lines.size(); a++) { if (tid != lines[a]->intProperty("arg0")) continue; // Vavoom things use the plane defined by the thing and its two // endpoints, based on the sector's original (flat) plane and treating // the thing's height as absolute if (MathStuff::distanceToLineFast(thing->point(), lines[a]->seg()) == 0) { LOG_MESSAGE(1, "Vavoom thing %d lies directly on its target line %d", thing->getIndex(), a); return; } short height = target->getPlaneHeight<p>(); fpoint3_t p1(thing->xPos(), thing->yPos(), thing->floatProperty("height")); fpoint3_t p2(lines[a]->x1(), lines[a]->y1(), height); fpoint3_t p3(lines[a]->x2(), lines[a]->y2(), height); target->setPlane<p>(MathStuff::planeFromTriangle(p1, p2, p3)); return; } LOG_MESSAGE(1, "Vavoom thing %d has no matching line with first arg %d", thing->getIndex(), tid); }
/* MapArchClipboardItem::addLines * Copies [lines] and all related map structures *******************************************************************/ void MapArchClipboardItem::addLines(vector<MapLine*> lines) { // Get sectors and sides to copy vector<MapSector*> copy_sectors; vector<MapSide*> copy_sides; for (unsigned a = 0; a < lines.size(); a++) { MapSide* s1 = lines[a]->s1(); MapSide* s2 = lines[a]->s2(); // Front side if (s1) { copy_sides.push_back(s1); if (std::find(copy_sectors.begin(), copy_sectors.end(), s1->getSector()) == copy_sectors.end()) copy_sectors.push_back(s1->getSector()); } // Back side if (s2) { copy_sides.push_back(s2); if (std::find(copy_sectors.begin(), copy_sectors.end(), s2->getSector()) == copy_sectors.end()) copy_sectors.push_back(s2->getSector()); } } // Copy sectors for (unsigned a = 0; a < copy_sectors.size(); a++) { MapSector* copy = new MapSector(NULL); copy->copy(copy_sectors[a]); sectors.push_back(copy); } // Copy sides for (unsigned a = 0; a < copy_sides.size(); a++) { MapSide* copy = new MapSide(); copy->copy(copy_sides[a]); // Set relative sector for (unsigned b = 0; b < copy_sectors.size(); b++) { if (copy_sides[a]->getSector() == copy_sectors[b]) { copy->setSector(sectors[b]); break; } } sides.push_back(copy); } // Get vertices to copy (and determine midpoint) double min_x = 9999999; double max_x = -9999999; double min_y = 9999999; double max_y = -9999999; vector<MapVertex*> copy_verts; for (unsigned a = 0; a < lines.size(); a++) { MapVertex* v1 = lines[a]->v1(); MapVertex* v2 = lines[a]->v2(); // Add vertices to copy list if (std::find(copy_verts.begin(), copy_verts.end(), v1) == copy_verts.end()) copy_verts.push_back(v1); if (std::find(copy_verts.begin(), copy_verts.end(), v2) == copy_verts.end()) copy_verts.push_back(v2); // Update min/max if (v1->xPos() < min_x) min_x = v1->xPos(); if (v1->xPos() > max_x) max_x = v1->xPos(); if (v1->yPos() < min_y) min_y = v1->yPos(); if (v1->yPos() > max_y) max_y = v1->yPos(); if (v2->xPos() < min_x) min_x = v2->xPos(); if (v2->xPos() > max_x) max_x = v2->xPos(); if (v2->yPos() < min_y) min_y = v2->yPos(); if (v2->yPos() > max_y) max_y = v2->yPos(); } // Determine midpoint double mid_x = min_x + ((max_x - min_x) * 0.5); double mid_y = min_y + ((max_y - min_y) * 0.5); this->midpoint.set(mid_x, mid_y); // Copy vertices for (unsigned a = 0; a < copy_verts.size(); a++) { MapVertex* copy = new MapVertex(copy_verts[a]->xPos() - mid_x, copy_verts[a]->yPos() - mid_y); copy->copy(copy_verts[a]); vertices.push_back(copy); } // Copy lines for (unsigned a = 0; a < lines.size(); a++) { // Get relative sides MapSide* s1 = NULL; MapSide* s2 = NULL; bool s1_found = false; bool s2_found = !(lines[a]->s2()); for (unsigned b = 0; b < copy_sides.size(); b++) { if (lines[a]->s1() == copy_sides[b]) { s1 = sides[b]; s1_found = true; } if (lines[a]->s2() == copy_sides[b]) { s2 = sides[b]; s2_found = true; } if (s1_found && s2_found) break; } // Get relative vertices MapVertex* v1 = NULL; MapVertex* v2 = NULL; for (unsigned b = 0; b < copy_verts.size(); b++) { if (lines[a]->v1() == copy_verts[b]) v1 = vertices[b]; if (lines[a]->v2() == copy_verts[b]) v2 = vertices[b]; if (v1 && v2) break; } // Copy line MapLine* copy = new MapLine(v1, v2, s1, s2); copy->copy(lines[a]); this->lines.push_back(copy); } }
/* MapSpecials::processEternitySlopes * Process Eternity slope specials *******************************************************************/ void MapSpecials::processEternitySlopes(SLADEMap* map) { // Eternity plans on having a few slope mechanisms, // which must be evaluated in a specific order. // - Plane_Align, in line order // - vertex triangle slopes, in sector order (wip) // - Plane_Copy, in line order // First things first: reset every sector to flat planes for(unsigned a = 0; a < map->nSectors(); a++) { MapSector* target = map->getSector(a); target->setPlane<FLOOR_PLANE>(plane_t::flat(target->getPlaneHeight<FLOOR_PLANE>())); target->setPlane<CEILING_PLANE>(plane_t::flat(target->getPlaneHeight<CEILING_PLANE>())); } // Plane_Align (line special 181) for(unsigned a = 0; a < map->nLines(); a++) { MapLine* line = map->getLine(a); if(line->getSpecial() != 181) continue; MapSector* sector1 = line->frontSector(); MapSector* sector2 = line->backSector(); if(!sector1 || !sector2) { LOG_MESSAGE(1, "Ignoring Plane_Align on one-sided line %d", line->getIndex()); continue; } if(sector1 == sector2) { LOG_MESSAGE(1, "Ignoring Plane_Align on line %d, which has the same sector on both sides", line->getIndex()); continue; } int floor_arg = line->intProperty("arg0"); if(floor_arg == 1) applyPlaneAlign<FLOOR_PLANE>(line, sector1, sector2); else if(floor_arg == 2) applyPlaneAlign<FLOOR_PLANE>(line, sector2, sector1); int ceiling_arg = line->intProperty("arg1"); if(ceiling_arg == 1) applyPlaneAlign<CEILING_PLANE>(line, sector1, sector2); else if(ceiling_arg == 2) applyPlaneAlign<CEILING_PLANE>(line, sector2, sector1); } // Plane_Copy vector<MapSector*> sectors; for(unsigned a = 0; a < map->nLines(); a++) { MapLine* line = map->getLine(a); if(line->getSpecial() != 118) continue; int tag; MapSector* front = line->frontSector(); MapSector* back = line->backSector(); if((tag = line->intProperty("arg0"))) { sectors.clear(); map->getSectorsByTag(tag, sectors); if(sectors.size()) front->setFloorPlane(sectors[0]->getFloorPlane()); } if((tag = line->intProperty("arg1"))) { sectors.clear(); map->getSectorsByTag(tag, sectors); if(sectors.size()) front->setCeilingPlane(sectors[0]->getCeilingPlane()); } if((tag = line->intProperty("arg2"))) { sectors.clear(); map->getSectorsByTag(tag, sectors); if(sectors.size()) back->setFloorPlane(sectors[0]->getFloorPlane()); } if((tag = line->intProperty("arg3"))) { sectors.clear(); map->getSectorsByTag(tag, sectors); if(sectors.size()) back->setCeilingPlane(sectors[0]->getCeilingPlane()); } // The fifth "share" argument copies from one side of the line to the // other if(front && back) { int share = line->intProperty("arg4"); if((share & 3) == 1) back->setFloorPlane(front->getFloorPlane()); else if((share & 3) == 2) front->setFloorPlane(back->getFloorPlane()); if((share & 12) == 4) back->setCeilingPlane(front->getCeilingPlane()); else if((share & 12) == 8) front->setCeilingPlane(back->getCeilingPlane()); } } }
/* MapSpecials::processZDoomSlopes * Process ZDoom slope specials *******************************************************************/ void MapSpecials::processZDoomSlopes(SLADEMap* map) { // ZDoom has a variety of slope mechanisms, which must be evaluated in a // specific order. // - Plane_Align, in line order // - line slope + sector tilt + vavoom, in thing order // - slope copy things, in thing order // - overwrite vertex heights with vertex height things // - vertex triangle slopes, in sector order // - Plane_Copy, in line order // First things first: reset every sector to flat planes for (unsigned a = 0; a < map->nSectors(); a++) { MapSector* target = map->getSector(a); target->setPlane<FLOOR_PLANE>(plane_t::flat(target->getPlaneHeight<FLOOR_PLANE>())); target->setPlane<CEILING_PLANE>(plane_t::flat(target->getPlaneHeight<CEILING_PLANE>())); } // Plane_Align (line special 181) for (unsigned a = 0; a < map->nLines(); a++) { MapLine* line = map->getLine(a); if (line->getSpecial() != 181) continue; MapSector* sector1 = line->frontSector(); MapSector* sector2 = line->backSector(); if (!sector1 || !sector2) { LOG_MESSAGE(1, "Ignoring Plane_Align on one-sided line %d", line->getIndex()); continue; } if (sector1 == sector2) { LOG_MESSAGE(1, "Ignoring Plane_Align on line %d, which has the same sector on both sides", line->getIndex()); continue; } int floor_arg = line->intProperty("arg0"); if (floor_arg == 1) applyPlaneAlign<FLOOR_PLANE>(line, sector1, sector2); else if (floor_arg == 2) applyPlaneAlign<FLOOR_PLANE>(line, sector2, sector1); int ceiling_arg = line->intProperty("arg1"); if (ceiling_arg == 1) applyPlaneAlign<CEILING_PLANE>(line, sector1, sector2); else if (ceiling_arg == 2) applyPlaneAlign<CEILING_PLANE>(line, sector2, sector1); } // Line slope things (9500/9501), sector tilt things (9502/9503), and // vavoom things (1500/1501), all in the same pass for (unsigned a = 0; a < map->nThings(); a++) { MapThing* thing = map->getThing(a); // Line slope things if (thing->getType() == 9500) applyLineSlopeThing<FLOOR_PLANE>(map, thing); else if (thing->getType() == 9501) applyLineSlopeThing<CEILING_PLANE>(map, thing); // Sector tilt things else if (thing->getType() == 9502) applySectorTiltThing<FLOOR_PLANE>(map, thing); else if (thing->getType() == 9503) applySectorTiltThing<CEILING_PLANE>(map, thing); // Vavoom things else if (thing->getType() == 1500) applyVavoomSlopeThing<FLOOR_PLANE>(map, thing); else if (thing->getType() == 1501) applyVavoomSlopeThing<CEILING_PLANE>(map, thing); } // Slope copy things (9510/9511) for (unsigned a = 0; a < map->nThings(); a++) { MapThing* thing = map->getThing(a); if (thing->getType() == 9510 || thing->getType() == 9511) { int target_idx = map->sectorAt(thing->point()); if (target_idx < 0) continue; MapSector* target = map->getSector(target_idx); // First argument is the tag of a sector whose slope should be copied int tag = thing->intProperty("arg0"); if (!tag) { LOG_MESSAGE(1, "Ignoring slope copy thing in sector %d with no argument", target_idx); continue; } vector<MapSector*> tagged_sectors; map->getSectorsByTag(tag, tagged_sectors); if (tagged_sectors.empty()) { LOG_MESSAGE(1, "Ignoring slope copy thing in sector %d; no sectors have target tag %d", target_idx, tag); continue; } if (thing->getType() == 9510) target->setFloorPlane(tagged_sectors[0]->getFloorPlane()); else target->setCeilingPlane(tagged_sectors[0]->getCeilingPlane()); } } // Vertex height things // These only affect the calculation of slopes and shouldn't be stored in // the map data proper, so instead of actually changing vertex properties, // we store them in a hashmap. VertexHeightMap vertex_floor_heights; VertexHeightMap vertex_ceiling_heights; for (unsigned a = 0; a < map->nThings(); a++) { MapThing* thing = map->getThing(a); if (thing->getType() == 1504 || thing->getType() == 1505) { // TODO there could be more than one vertex at this point MapVertex* vertex = map->vertexAt(thing->xPos(), thing->yPos()); if (vertex) { if (thing->getType() == 1504) vertex_floor_heights[vertex] = thing->floatProperty("height"); else if (thing->getType() == 1505) vertex_ceiling_heights[vertex] = thing->floatProperty("height"); } } } // Vertex heights -- only applies for sectors with exactly three vertices. // Heights may be set by UDMF properties, or by a vertex height thing // placed exactly on the vertex (which takes priority over the prop). vector<MapVertex*> vertices; for (unsigned a = 0; a < map->nSectors(); a++) { MapSector* target = map->getSector(a); vertices.clear(); target->getVertices(vertices); if (vertices.size() != 3) continue; applyVertexHeightSlope<FLOOR_PLANE>(target, vertices, vertex_floor_heights); applyVertexHeightSlope<CEILING_PLANE>(target, vertices, vertex_ceiling_heights); } // Plane_Copy vector<MapSector*> sectors; for (unsigned a = 0; a < map->nLines(); a++) { MapLine* line = map->getLine(a); if (line->getSpecial() != 118) continue; int tag; MapSector* front = line->frontSector(); MapSector* back = line->backSector(); if ((tag = line->intProperty("arg0")) && front) { sectors.clear(); map->getSectorsByTag(tag, sectors); if (sectors.size()) front->setFloorPlane(sectors[0]->getFloorPlane()); } if ((tag = line->intProperty("arg1")) && front) { sectors.clear(); map->getSectorsByTag(tag, sectors); if (sectors.size()) front->setCeilingPlane(sectors[0]->getCeilingPlane()); } if ((tag = line->intProperty("arg2")) && back) { sectors.clear(); map->getSectorsByTag(tag, sectors); if (sectors.size()) back->setFloorPlane(sectors[0]->getFloorPlane()); } if ((tag = line->intProperty("arg3")) && back) { sectors.clear(); map->getSectorsByTag(tag, sectors); if (sectors.size()) back->setCeilingPlane(sectors[0]->getCeilingPlane()); } // The fifth "share" argument copies from one side of the line to the // other if (front && back) { int share = line->intProperty("arg4"); if ((share & 3) == 1) back->setFloorPlane(front->getFloorPlane()); else if ((share & 3) == 2) front->setFloorPlane(back->getFloorPlane()); if ((share & 12) == 4) back->setCeilingPlane(front->getCeilingPlane()); else if ((share & 12) == 8) front->setCeilingPlane(back->getCeilingPlane()); } } }
MapBlock* Database_Dummy::loadBlock(v3s16 blockpos) { v2s16 p2d(blockpos.X, blockpos.Z); if(m_database.count(getBlockAsInteger(blockpos))) { /* Make sure sector is loaded */ MapSector *sector = srvmap->createSector(p2d); /* Load block */ std::string datastr = m_database[getBlockAsInteger(blockpos)]; // srvmap->loadBlock(&datastr, blockpos, sector, false); try { std::istringstream is(datastr, std::ios_base::binary); u8 version = SER_FMT_VER_INVALID; is.read((char*)&version, 1); if(is.fail()) throw SerializationError("ServerMap::loadBlock(): Failed" " to read MapBlock version"); MapBlock *block = NULL; bool created_new = false; block = sector->getBlockNoCreateNoEx(blockpos.Y); if(block == NULL) { block = sector->createBlankBlockNoInsert(blockpos.Y); created_new = true; } // Read basic data block->deSerialize(is, version, true); // If it's a new block, insert it to the map if(created_new) sector->insertBlock(block); /* Save blocks loaded in old format in new format */ //if(version < SER_FMT_VER_HIGHEST || save_after_load) // Only save if asked to; no need to update version //if(save_after_load) // saveBlock(block); // We just loaded it from, so it's up-to-date. block->resetModified(); } catch(SerializationError &e) { errorstream<<"Invalid block data in database" <<" ("<<blockpos.X<<","<<blockpos.Y<<","<<blockpos.Z<<")" <<" (SerializationError): "<<e.what()<<std::endl; // TODO: Block should be marked as invalid in memory so that it is // not touched but the game can run if(g_settings->getBool("ignore_world_load_errors")){ errorstream<<"Ignoring block load error. Duck and cover! " <<"(ignore_world_load_errors)"<<std::endl; } else { throw SerializationError("Invalid block data in database"); //assert(0); } } return srvmap->getBlockNoCreateNoEx(blockpos); // should not be using this here } return(NULL); }
/* InfoOverlay3D::update * Updates the info text for the object of [item_type] at [item_index] * in [map] *******************************************************************/ void InfoOverlay3D::update(int item_index, int item_type, SLADEMap* map) { // Clear current info info.clear(); info2.clear(); // Setup variables current_type = item_type; texname = ""; texture = NULL; thing_icon = false; int map_format = theMapEditor->currentMapDesc().format; // Wall if (item_type == MapEditor::SEL_SIDE_BOTTOM || item_type == MapEditor::SEL_SIDE_MIDDLE || item_type == MapEditor::SEL_SIDE_TOP) { // Get line and side MapSide* side = map->getSide(item_index); if (!side) return; MapLine* line = side->getParentLine(); if (!line) return; object = side; // --- Line/side info --- info.push_back(S_FMT("Line #%d", line->getIndex())); if (side == line->s1()) info.push_back(S_FMT("Front Side #%d", side->getIndex())); else info.push_back(S_FMT("Back Side #%d", side->getIndex())); // Relevant flags string flags = ""; if (theGameConfiguration->lineBasicFlagSet("dontpegtop", line, map_format)) flags += "Upper Unpegged, "; if (theGameConfiguration->lineBasicFlagSet("dontpegbottom", line, map_format)) flags += "Lower Unpegged, "; if (theGameConfiguration->lineBasicFlagSet("blocking", line, map_format)) flags += "Blocking, "; if (!flags.IsEmpty()) flags.RemoveLast(2); info.push_back(flags); info.push_back(S_FMT("Length: %d", (int)line->getLength())); // Other potential info: special, sector# // --- Wall part info --- // Part if (item_type == MapEditor::SEL_SIDE_BOTTOM) info2.push_back("Lower Texture"); else if (item_type == MapEditor::SEL_SIDE_MIDDLE) info2.push_back("Middle Texture"); else info2.push_back("Upper Texture"); // Offsets if (theGameConfiguration->udmfNamespace() == "zdoom") { // Get x offset info int xoff = side->intProperty("offsetx"); double xoff_part = 0; if (item_type == MapEditor::SEL_SIDE_BOTTOM) xoff_part = side->floatProperty("offsetx_bottom"); else if (item_type == MapEditor::SEL_SIDE_MIDDLE) xoff_part = side->floatProperty("offsetx_mid"); else xoff_part = side->floatProperty("offsetx_top"); // Add x offset string string xoff_info; if (xoff_part == 0) xoff_info = S_FMT("%d", xoff); else if (xoff_part > 0) xoff_info = S_FMT("%1.2f (%d+%1.2f)", (double)xoff+xoff_part, xoff, xoff_part); else xoff_info = S_FMT("%1.2f (%d-%1.2f)", (double)xoff+xoff_part, xoff, -xoff_part); // Get y offset info int yoff = side->intProperty("offsety"); double yoff_part = 0; if (item_type == MapEditor::SEL_SIDE_BOTTOM) yoff_part = side->floatProperty("offsety_bottom"); else if (item_type == MapEditor::SEL_SIDE_MIDDLE) yoff_part = side->floatProperty("offsety_mid"); else yoff_part = side->floatProperty("offsety_top"); // Add y offset string string yoff_info; if (yoff_part == 0) yoff_info = S_FMT("%d", yoff); else if (yoff_part > 0) yoff_info = S_FMT("%1.2f (%d+%1.2f)", (double)yoff+yoff_part, yoff, yoff_part); else yoff_info = S_FMT("%1.2f (%d-%1.2f)", (double)yoff+yoff_part, yoff, -yoff_part); info2.push_back(S_FMT("Offsets: %s, %s", xoff_info, yoff_info)); } else { // Basic offsets info2.push_back(S_FMT("Offsets: %d, %d", side->intProperty("offsetx"), side->intProperty("offsety"))); } // ZDoom UDMF extras if (theGameConfiguration->udmfNamespace() == "zdoom") { // Scale double xscale, yscale; if (item_type == MapEditor::SEL_SIDE_BOTTOM) { xscale = side->floatProperty("scalex_bottom"); yscale = side->floatProperty("scaley_bottom"); } else if (item_type == MapEditor::SEL_SIDE_MIDDLE) { xscale = side->floatProperty("scalex_mid"); yscale = side->floatProperty("scaley_mid"); } else { xscale = side->floatProperty("scalex_top"); yscale = side->floatProperty("scaley_top"); } info2.push_back(S_FMT("Scale: %1.2fx, %1.2fx", xscale, yscale)); } else { info2.push_back(""); } // Height of this section of the wall // TODO this is wrong in the case of slopes, but slope support only // exists in the 3.1.1 branch fpoint2_t left_point, right_point; MapSide* other_side; if (side == line->s1()) { left_point = line->v1()->getPoint(0); right_point = line->v2()->getPoint(0); other_side = line->s2(); } else { left_point = line->v2()->getPoint(0); right_point = line->v1()->getPoint(0); other_side = line->s1(); } MapSector* this_sector = side->getSector(); MapSector* other_sector = NULL; if (other_side) other_sector = other_side->getSector(); double left_height, right_height; if (item_type == MapEditor::SEL_SIDE_MIDDLE && other_sector) { // A two-sided line's middle area is the smallest distance between // both sides' floors and ceilings, which is more complicated with // slopes. plane_t floor1 = this_sector->getFloorPlane(); plane_t floor2 = other_sector->getFloorPlane(); plane_t ceiling1 = this_sector->getCeilingPlane(); plane_t ceiling2 = other_sector->getCeilingPlane(); left_height = min(ceiling1.height_at(left_point), ceiling2.height_at(left_point)) - max(floor1.height_at(left_point), floor2.height_at(left_point)); right_height = min(ceiling1.height_at(right_point), ceiling2.height_at(right_point)) - max(floor1.height_at(right_point), floor2.height_at(right_point)); } else { plane_t top_plane, bottom_plane; if (item_type == MapEditor::SEL_SIDE_MIDDLE) { top_plane = this_sector->getCeilingPlane(); bottom_plane = this_sector->getFloorPlane(); } else { if (!other_sector) return; if (item_type == MapEditor::SEL_SIDE_TOP) { top_plane = this_sector->getCeilingPlane(); bottom_plane = other_sector->getCeilingPlane(); } else { top_plane = other_sector->getFloorPlane(); bottom_plane = this_sector->getFloorPlane(); } } left_height = top_plane.height_at(left_point) - bottom_plane.height_at(left_point); right_height = top_plane.height_at(right_point) - bottom_plane.height_at(right_point); } if (fabs(left_height - right_height) < 0.001) info2.push_back(S_FMT("Height: %d", (int)left_height)); else info2.push_back(S_FMT("Height: %d ~ %d", (int)left_height, (int)right_height)); // Texture if (item_type == MapEditor::SEL_SIDE_BOTTOM) texname = side->getTexLower(); else if (item_type == MapEditor::SEL_SIDE_MIDDLE) texname = side->getTexMiddle(); else texname = side->getTexUpper(); texture = theMapEditor->textureManager().getTexture(texname, theGameConfiguration->mixTexFlats()); } // Floor else if (item_type == MapEditor::SEL_FLOOR || item_type == MapEditor::SEL_CEILING) { // Get sector MapSector* sector = map->getSector(item_index); if (!sector) return; object = sector; // Get basic info int fheight = sector->intProperty("heightfloor"); int cheight = sector->intProperty("heightceiling"); // --- Sector info --- // Sector index info.push_back(S_FMT("Sector #%d", item_index)); // Sector height info.push_back(S_FMT("Total Height: %d", cheight - fheight)); // ZDoom UDMF extras /* if (theGameConfiguration->udmfNamespace() == "zdoom") { // Sector colour rgba_t col = sector->getColour(0, true); info.push_back(S_FMT("Colour: R%d, G%d, B%d", col.r, col.g, col.b)); } */ // --- Flat info --- // Height if (item_type == MapEditor::SEL_FLOOR) info2.push_back(S_FMT("Floor Height: %d", fheight)); else info2.push_back(S_FMT("Ceiling Height: %d", cheight)); // Light int light = sector->intProperty("lightlevel"); if (theGameConfiguration->udmfNamespace() == "zdoom") { // Get extra light info int fl = 0; bool abs = false; if (item_type == MapEditor::SEL_FLOOR) { fl = sector->intProperty("lightfloor"); abs = sector->boolProperty("lightfloorabsolute"); } else { fl = sector->intProperty("lightceiling"); abs = sector->boolProperty("lightceilingabsolute"); } // Set if absolute if (abs) { light = fl; fl = 0; } // Add info string if (fl == 0) info2.push_back(S_FMT("Light: %d", light)); else if (fl > 0) info2.push_back(S_FMT("Light: %d (%d+%d)", light+fl, light, fl)); else info2.push_back(S_FMT("Light: %d (%d-%d)", light+fl, light, -fl)); } else info2.push_back(S_FMT("Light: %d", light)); // ZDoom UDMF extras if (theGameConfiguration->udmfNamespace() == "zdoom") { // Offsets double xoff, yoff; if (item_type == MapEditor::SEL_FLOOR) { xoff = sector->floatProperty("xpanningfloor"); yoff = sector->floatProperty("ypanningfloor"); } else { xoff = sector->floatProperty("xpanningceiling"); yoff = sector->floatProperty("ypanningceiling"); } info2.push_back(S_FMT("Offsets: %1.2f, %1.2f", xoff, yoff)); // Scaling double xscale, yscale; if (item_type == MapEditor::SEL_FLOOR) { xscale = sector->floatProperty("xscalefloor"); yscale = sector->floatProperty("yscalefloor"); } else { xscale = sector->floatProperty("xscaleceiling"); yscale = sector->floatProperty("yscaleceiling"); } info2.push_back(S_FMT("Scale: %1.2fx, %1.2fx", xscale, yscale)); } // Texture if (item_type == MapEditor::SEL_FLOOR) texname = sector->getFloorTex(); else texname = sector->getCeilingTex(); texture = theMapEditor->textureManager().getFlat(texname, theGameConfiguration->mixTexFlats()); } // Thing else if (item_type == MapEditor::SEL_THING) { // index, type, position, sector, zpos, height?, radius? // Get thing MapThing* thing = map->getThing(item_index); if (!thing) return; object = thing; // Index info.push_back(S_FMT("Thing #%d", item_index)); // Position if (theMapEditor->currentMapDesc().format == MAP_HEXEN || theMapEditor->currentMapDesc().format == MAP_UDMF) info.push_back(S_FMT("Position: %d, %d, %d", (int)thing->xPos(), (int)thing->yPos(), (int)thing->floatProperty("height"))); else info.push_back(S_FMT("Position: %d, %d", (int)thing->xPos(), (int)thing->yPos())); // Type ThingType* tt = theGameConfiguration->thingType(thing->getType()); if (tt->getName() == "Unknown") info2.push_back(S_FMT("Type: %d", thing->getType())); else info2.push_back(S_FMT("Type: %s", tt->getName())); // Args if (theMapEditor->currentMapDesc().format == MAP_HEXEN || (theMapEditor->currentMapDesc().format == MAP_UDMF && theGameConfiguration->getUDMFProperty("arg0", MOBJ_THING))) { // Get thing args int args[5]; args[0] = thing->intProperty("arg0"); args[1] = thing->intProperty("arg1"); args[2] = thing->intProperty("arg2"); args[3] = thing->intProperty("arg3"); args[4] = thing->intProperty("arg4"); string argstr = tt->getArgsString(args); if (argstr.IsEmpty()) info2.push_back("No Args"); else info2.push_back(argstr); } // Sector int sector = map->sectorAt(thing->point()); if (sector >= 0) info2.push_back(S_FMT("In Sector #%d", sector)); else info2.push_back("No Sector"); // Texture texture = theMapEditor->textureManager().getSprite(tt->getSprite(), tt->getTranslation(), tt->getPalette()); if (!texture) { if (use_zeth_icons && tt->getZeth() >= 0) texture = theMapEditor->textureManager().getEditorImage(S_FMT("zethicons/zeth%02d", tt->getZeth())); if (!texture) texture = theMapEditor->textureManager().getEditorImage(S_FMT("thing/%s", tt->getIcon())); thing_icon = true; } texname = ""; } last_update = theApp->runTimer(); }
MapBlock* Database_SQLite3::loadBlock(v3s16 blockpos) { v2s16 p2d(blockpos.X, blockpos.Z); verifyDatabase(); if(sqlite3_bind_int64(m_database_read, 1, getBlockAsInteger(blockpos)) != SQLITE_OK) infostream<<"WARNING: Could not bind block position for load: " <<sqlite3_errmsg(m_database)<<std::endl; if(sqlite3_step(m_database_read) == SQLITE_ROW) { /* Make sure sector is loaded */ MapSector *sector = srvmap->createSector(p2d); /* Load block */ const char * data = (const char *)sqlite3_column_blob(m_database_read, 0); size_t len = sqlite3_column_bytes(m_database_read, 0); std::string datastr(data, len); // srvmap->loadBlock(&datastr, blockpos, sector, false); try { std::istringstream is(datastr, std::ios_base::binary); u8 version = SER_FMT_VER_INVALID; is.read((char*)&version, 1); if(is.fail()) throw SerializationError("ServerMap::loadBlock(): Failed" " to read MapBlock version"); MapBlock *block = NULL; bool created_new = false; block = sector->getBlockNoCreateNoEx(blockpos.Y); if(block == NULL) { block = sector->createBlankBlockNoInsert(blockpos.Y); created_new = true; } // Read basic data block->deSerialize(is, version, true); // If it's a new block, insert it to the map if(created_new) sector->insertBlock(block); /* Save blocks loaded in old format in new format */ //if(version < SER_FMT_VER_HIGHEST || save_after_load) // Only save if asked to; no need to update version //if(save_after_load) // saveBlock(block); // We just loaded it from, so it's up-to-date. block->resetModified(); } catch(SerializationError &e) { errorstream<<"Invalid block data in database" <<" ("<<blockpos.X<<","<<blockpos.Y<<","<<blockpos.Z<<")" <<" (SerializationError): "<<e.what()<<std::endl; // TODO: Block should be marked as invalid in memory so that it is // not touched but the game can run if(g_settings->getBool("ignore_world_load_errors")){ errorstream<<"Ignoring block load error. Duck and cover! " <<"(ignore_world_load_errors)"<<std::endl; } else { throw SerializationError("Invalid block data in database"); //assert(0); } } sqlite3_step(m_database_read); // We should never get more than 1 row, so ok to reset sqlite3_reset(m_database_read); return srvmap->getBlockNoCreateNoEx(blockpos); // should not be using this here } sqlite3_reset(m_database_read); return(NULL); }
/* MapEditContext::floodFill3d * Pastes previously copied wall/flat/thing info to selection and ad *******************************************************************/ void Edit3D::floodFill(CopyType type) { // Get items to paste to auto& selection = context_.selection(); auto items = getAdjacent(selection.hilight()); // Restrict floodfill to selection, if any if (selection.size() > 0) { for (auto i = items.begin(); i < items.end(); ++i) { bool found = false; for (unsigned a = 0; a < selection.size(); a++) { if (selection[a].type == i->type && selection[a].index == i->index) { found = true; break; } } if (!found) items.erase(i); } } // Begin undo step string ptype = "Floodfill textures"; undo_manager_->beginRecord(ptype); // Go through items for (unsigned a = 0; a < items.size(); a++) { // Wall if (items[a].type == ItemType::WallTop || items[a].type == ItemType::WallMiddle || items[a].type == ItemType::WallBottom) { auto side = context_.map().getSide(items[a].index); undo_manager_->recordUndoStep(new MapEditor::PropertyChangeUS(side)); // Upper wall if (items[a].type == ItemType::WallTop) { // Texture if (type == CopyType::TexType) side->setStringProperty("texturetop", copy_texture_); } // Middle wall else if (items[a].type == ItemType::WallMiddle) { // Texture if (type == CopyType::TexType) side->setStringProperty("texturemiddle", copy_texture_); } // Lower wall else if (items[a].type == ItemType::WallBottom) { // Texture if (type == CopyType::TexType) side->setStringProperty("texturebottom", copy_texture_); } } // Flat else if (items[a].type == ItemType::Floor || items[a].type == ItemType::Ceiling) { MapSector* sector = context_.map().getSector(items[a].index); undo_manager_->recordUndoStep(new MapEditor::PropertyChangeUS(sector)); // Floor if (items[a].type == ItemType::Floor) { // Texture if (type == CopyType::TexType) sector->setStringProperty("texturefloor", copy_texture_); } // Ceiling if (items[a].type == ItemType::Ceiling) { // Texture if (type == CopyType::TexType) sector->setStringProperty("textureceiling", copy_texture_); } } } // Editor message if (type == CopyType::TexType) { context_.addEditorMessage("Floodfilled Texture"); } undo_manager_->endRecord(true); }
/* MapEditContext::paste3d * Pastes previously copied wall/flat/thing info to selection *******************************************************************/ void Edit3D::paste(CopyType type) { // Begin undo step string ptype = "Paste Properties"; if (type == CopyType::TexType) ptype = "Paste Texture/Type"; undo_manager_->beginRecord(ptype); // Go through items auto& selection = context_.selection(); for (auto& item : selection.selectionOrHilight()) { // Wall if (MapEditor::baseItemType(item.type) == ItemType::Side) { MapSide* side = context_.map().getSide(item.index); undo_manager_->recordUndoStep(new MapEditor::PropertyChangeUS(side)); // Upper wall if (item.type == ItemType::WallTop) { // Texture if (type == CopyType::TexType) side->setStringProperty("texturetop", copy_texture_); } // Middle wall else if (item.type == ItemType::WallMiddle) { // Texture if (type == CopyType::TexType) side->setStringProperty("texturemiddle", copy_texture_); } // Lower wall else if (item.type == ItemType::WallBottom) { // Texture if (type == CopyType::TexType) side->setStringProperty("texturebottom", copy_texture_); } } // Flat else if (item.type == ItemType::Floor || item.type == ItemType::Ceiling) { MapSector* sector = context_.map().getSector(item.index); undo_manager_->recordUndoStep(new MapEditor::PropertyChangeUS(sector)); // Floor if (item.type == ItemType::Floor) { // Texture if (type == CopyType::TexType) sector->setStringProperty("texturefloor", copy_texture_); } // Ceiling if (item.type == ItemType::Ceiling) { // Texture if (type == CopyType::TexType) sector->setStringProperty("textureceiling", copy_texture_); } } // Thing else if (item.type == ItemType::Thing) { MapThing* thing = context_.map().getThing(item.index); undo_manager_->recordUndoStep(new MapEditor::PropertyChangeUS(thing)); // Type if (type == CopyType::TexType) thing->setIntProperty("type", copy_thing_.getType()); } } // Editor message if (type == CopyType::TexType) { if (selection.hilight().type == ItemType::Thing) context_.addEditorMessage("Pasted Thing Type"); else context_.addEditorMessage("Pasted Texture"); } undo_manager_->endRecord(true); }
/* Edit3D::changeOffset * Changes the offset of selected walls by [amount]. X axis if [x] is * true, otherwise Y axis *******************************************************************/ void Edit3D::changeOffset(int amount, bool x) const { // Get items to process vector<MapEditor::Item> items; auto& selection_3d = context_.selection(); auto hilight_3d = context_.hilightItem(); if (selection_3d.empty()) { if (hilight_3d.index >= 0 && hilight_3d.type != ItemType::Thing) items.push_back(hilight_3d); } else { for (unsigned a = 0; a < selection_3d.size(); a++) { if (selection_3d[a].type != ItemType::Thing) items.push_back(selection_3d[a]); } } if (items.empty()) return; // Begin undo level context_.beginUndoRecordLocked("Change Offset", true, false, false); // Go through items vector<int> done; bool changed = false; for (unsigned a = 0; a < items.size(); a++) { // Wall if (items[a].type >= ItemType::WallTop && items[a].type <= ItemType::WallBottom) { MapSide* side = context_.map().getSide(items[a].index); // If offsets are linked, just change the whole side offset if (link_offset_) { // Check we haven't processed this side already if (VECTOR_EXISTS(done, items[a].index)) continue; // Change the appropriate offset if (x) { int offset = side->intProperty("offsetx"); side->setIntProperty("offsetx", offset + amount); } else { int offset = side->intProperty("offsety"); side->setIntProperty("offsety", offset + amount); } // Add to done list done.push_back(items[a].index); } // Unlinked offsets else { // Build property string (offset[x/y]_[top/mid/bottom]) string ofs = "offsetx"; if (!x) ofs = "offsety"; if (items[a].type == ItemType::WallBottom) ofs += "_bottom"; else if (items[a].type == ItemType::WallTop) ofs += "_top"; else ofs += "_mid"; // Change the offset int offset = side->floatProperty(ofs); side->setFloatProperty(ofs, offset + amount); } changed = true; } // Flat (UDMF only) else { MapSector* sector = context_.map().getSector(items[a].index); if (Game::configuration().featureSupported(Game::UDMFFeature::FlatPanning)) { if (items[a].type == ItemType::Floor) { if (x) { double offset = sector->floatProperty("xpanningfloor"); sector->setFloatProperty("xpanningfloor", offset + amount); } else { double offset = sector->floatProperty("ypanningfloor"); sector->setFloatProperty("ypanningfloor", offset + amount); } changed = true; } else if (items[a].type == ItemType::Ceiling) { if (x) { double offset = sector->floatProperty("xpanningceiling"); sector->setFloatProperty("xpanningceiling", offset + amount); } else { double offset = sector->floatProperty("ypanningceiling"); sector->setFloatProperty("ypanningceiling", offset + amount); } changed = true; } } } } // End undo level context_.endUndoRecord(changed); // Editor message if (items.size() > 0 && changed) { string axis = "X"; if (!x) axis = "Y"; if (amount > 0) context_.addEditorMessage(S_FMT("%s offset increased by %d", axis, amount)); else context_.addEditorMessage(S_FMT("%s offset decreased by %d", axis, -amount)); } }
void ClientMap::updateDrawList(video::IVideoDriver* driver) { ScopeProfiler sp(g_profiler, "CM::updateDrawList()", SPT_AVG); g_profiler->add("CM::updateDrawList() count", 1); for (std::map<v3s16, MapBlock*>::iterator i = m_drawlist.begin(); i != m_drawlist.end(); ++i) { MapBlock *block = i->second; block->refDrop(); } m_drawlist.clear(); v3f camera_position = m_camera_position; v3f camera_direction = m_camera_direction; f32 camera_fov = m_camera_fov; // Use a higher fov to accomodate faster camera movements. // Blocks are cropped better when they are drawn. // Or maybe they aren't? Well whatever. camera_fov *= 1.2; v3s16 cam_pos_nodes = floatToInt(camera_position, BS); v3s16 p_blocks_min; v3s16 p_blocks_max; getBlocksInViewRange(cam_pos_nodes, &p_blocks_min, &p_blocks_max); // Number of blocks in rendering range u32 blocks_in_range = 0; // Number of blocks occlusion culled u32 blocks_occlusion_culled = 0; // Number of blocks in rendering range but don't have a mesh u32 blocks_in_range_without_mesh = 0; // Blocks that had mesh that would have been drawn according to // rendering range (if max blocks limit didn't kick in) u32 blocks_would_have_drawn = 0; // Blocks that were drawn and had a mesh u32 blocks_drawn = 0; // Blocks which had a corresponding meshbuffer for this pass //u32 blocks_had_pass_meshbuf = 0; // Blocks from which stuff was actually drawn //u32 blocks_without_stuff = 0; // Distance to farthest drawn block float farthest_drawn = 0; // No occlusion culling when free_move is on and camera is // inside ground bool occlusion_culling_enabled = true; if (g_settings->getBool("free_move")) { MapNode n = getNodeNoEx(cam_pos_nodes); if (n.getContent() == CONTENT_IGNORE || m_nodedef->get(n).solidness == 2) occlusion_culling_enabled = false; } for (std::map<v2s16, MapSector*>::iterator si = m_sectors.begin(); si != m_sectors.end(); ++si) { MapSector *sector = si->second; v2s16 sp = sector->getPos(); if (m_control.range_all == false) { if (sp.X < p_blocks_min.X || sp.X > p_blocks_max.X || sp.Y < p_blocks_min.Z || sp.Y > p_blocks_max.Z) continue; } MapBlockVect sectorblocks; sector->getBlocks(sectorblocks); /* Loop through blocks in sector */ u32 sector_blocks_drawn = 0; for (MapBlockVect::iterator i = sectorblocks.begin(); i != sectorblocks.end(); ++i) { MapBlock *block = *i; /* Compare block position to camera position, skip if not seen on display */ if (block->mesh) block->mesh->updateCameraOffset(m_camera_offset); float range = 100000 * BS; if (!m_control.range_all) range = m_control.wanted_range * BS; float d = 0.0; if (!isBlockInSight(block->getPos(), camera_position, camera_direction, camera_fov, range, &d)) continue; blocks_in_range++; /* Ignore if mesh doesn't exist */ if (!block->mesh) { blocks_in_range_without_mesh++; continue; } /* Occlusion culling */ if (occlusion_culling_enabled && isBlockOccluded(block, cam_pos_nodes)) { blocks_occlusion_culled++; continue; } // This block is in range. Reset usage timer. block->resetUsageTimer(); // Limit block count in case of a sudden increase blocks_would_have_drawn++; if (blocks_drawn >= m_control.wanted_max_blocks && !m_control.range_all && d > m_control.wanted_range * BS) continue; // Add to set block->refGrab(); m_drawlist[block->getPos()] = block; sector_blocks_drawn++; blocks_drawn++; if (d / BS > farthest_drawn) farthest_drawn = d / BS; } // foreach sectorblocks if (sector_blocks_drawn != 0) m_last_drawn_sectors.insert(sp); } m_control.blocks_would_have_drawn = blocks_would_have_drawn; m_control.blocks_drawn = blocks_drawn; m_control.farthest_drawn = farthest_drawn; g_profiler->avg("CM: blocks in range", blocks_in_range); g_profiler->avg("CM: blocks occlusion culled", blocks_occlusion_culled); if (blocks_in_range != 0) g_profiler->avg("CM: blocks in range without mesh (frac)", (float)blocks_in_range_without_mesh / blocks_in_range); g_profiler->avg("CM: blocks drawn", blocks_drawn); g_profiler->avg("CM: farthest drawn", farthest_drawn); g_profiler->avg("CM: wanted max blocks", m_control.wanted_max_blocks); }
int main(int argc, char *argv[]) { int retval = 0; /* Initialization */ log_add_output_maxlev(&main_stderr_log_out, LMT_ACTION); log_add_output_all_levs(&main_dstream_no_stderr_log_out); log_register_thread("main"); /* Parse command line */ // List all allowed options std::map<std::string, ValueSpec> allowed_options; allowed_options.insert(std::make_pair("help", ValueSpec(VALUETYPE_FLAG, _("Show allowed options")))); allowed_options.insert(std::make_pair("version", ValueSpec(VALUETYPE_FLAG, _("Show version information")))); allowed_options.insert(std::make_pair("config", ValueSpec(VALUETYPE_STRING, _("Load configuration from specified file")))); allowed_options.insert(std::make_pair("port", ValueSpec(VALUETYPE_STRING, _("Set network port (UDP)")))); allowed_options.insert(std::make_pair("disable-unittests", ValueSpec(VALUETYPE_FLAG, _("Disable unit tests")))); allowed_options.insert(std::make_pair("enable-unittests", ValueSpec(VALUETYPE_FLAG, _("Enable unit tests")))); allowed_options.insert(std::make_pair("map-dir", ValueSpec(VALUETYPE_STRING, _("Same as --world (deprecated)")))); allowed_options.insert(std::make_pair("world", ValueSpec(VALUETYPE_STRING, _("Set world path (implies local game) ('list' lists all)")))); allowed_options.insert(std::make_pair("worldname", ValueSpec(VALUETYPE_STRING, _("Set world by name (implies local game)")))); allowed_options.insert(std::make_pair("info", ValueSpec(VALUETYPE_FLAG, _("Print more information to console")))); allowed_options.insert(std::make_pair("verbose", ValueSpec(VALUETYPE_FLAG, _("Print even more information to console")))); allowed_options.insert(std::make_pair("trace", ValueSpec(VALUETYPE_FLAG, _("Print enormous amounts of information to log and console")))); allowed_options.insert(std::make_pair("logfile", ValueSpec(VALUETYPE_STRING, _("Set logfile path ('' = no logging)")))); allowed_options.insert(std::make_pair("gameid", ValueSpec(VALUETYPE_STRING, _("Set gameid (\"--gameid list\" prints available ones)")))); allowed_options.insert(std::make_pair("migrate", ValueSpec(VALUETYPE_STRING, _("Migrate from current map backend to another (Only works when using minetestserver or with --server)")))); #ifndef SERVER allowed_options.insert(std::make_pair("videomodes", ValueSpec(VALUETYPE_FLAG, _("Show available video modes")))); allowed_options.insert(std::make_pair("speedtests", ValueSpec(VALUETYPE_FLAG, _("Run speed tests")))); allowed_options.insert(std::make_pair("address", ValueSpec(VALUETYPE_STRING, _("Address to connect to. ('' = local game)")))); allowed_options.insert(std::make_pair("random-input", ValueSpec(VALUETYPE_FLAG, _("Enable random user input, for testing")))); allowed_options.insert(std::make_pair("server", ValueSpec(VALUETYPE_FLAG, _("Run dedicated server")))); allowed_options.insert(std::make_pair("name", ValueSpec(VALUETYPE_STRING, _("Set player name")))); allowed_options.insert(std::make_pair("password", ValueSpec(VALUETYPE_STRING, _("Set password")))); allowed_options.insert(std::make_pair("go", ValueSpec(VALUETYPE_FLAG, _("Disable main menu")))); #endif Settings cmd_args; bool ret = cmd_args.parseCommandLine(argc, argv, allowed_options); if(ret == false || cmd_args.getFlag("help") || cmd_args.exists("nonopt1")) { dstream<<_("Allowed options:")<<std::endl; for(std::map<std::string, ValueSpec>::iterator i = allowed_options.begin(); i != allowed_options.end(); ++i) { std::ostringstream os1(std::ios::binary); os1<<" --"<<i->first; if(i->second.type == VALUETYPE_FLAG) {} else os1<<_(" <value>"); dstream<<padStringRight(os1.str(), 24); if(i->second.help != NULL) dstream<<i->second.help; dstream<<std::endl; } return cmd_args.getFlag("help") ? 0 : 1; } if(cmd_args.getFlag("version")) { #ifdef SERVER dstream<<"minetestserver "<<minetest_version_hash<<std::endl; #else dstream<<"Minetest "<<minetest_version_hash<<std::endl; dstream<<"Using Irrlicht "<<IRRLICHT_SDK_VERSION<<std::endl; #endif dstream<<"Build info: "<<minetest_build_info<<std::endl; return 0; } /* Low-level initialization */ // If trace is enabled, enable logging of certain things if(cmd_args.getFlag("trace")){ dstream<<_("Enabling trace level debug output")<<std::endl; log_trace_level_enabled = true; dout_con_ptr = &verbosestream; // this is somewhat old crap socket_enable_debug_output = true; // socket doesn't use log.h } // In certain cases, output info level on stderr if(cmd_args.getFlag("info") || cmd_args.getFlag("verbose") || cmd_args.getFlag("trace") || cmd_args.getFlag("speedtests")) log_add_output(&main_stderr_log_out, LMT_INFO); // In certain cases, output verbose level on stderr if(cmd_args.getFlag("verbose") || cmd_args.getFlag("trace")) log_add_output(&main_stderr_log_out, LMT_VERBOSE); porting::signal_handler_init(); bool &kill = *porting::signal_handler_killstatus(); porting::initializePaths(); // Create user data directory fs::CreateDir(porting::path_user); infostream<<"path_share = "<<porting::path_share<<std::endl; infostream<<"path_user = "******"gameid") && cmd_args.get("gameid") == "list") { std::set<std::string> gameids = getAvailableGameIds(); for(std::set<std::string>::const_iterator i = gameids.begin(); i != gameids.end(); i++) dstream<<(*i)<<std::endl; return 0; } // List worlds if requested if(cmd_args.exists("world") && cmd_args.get("world") == "list"){ dstream<<_("Available worlds:")<<std::endl; std::vector<WorldSpec> worldspecs = getAvailableWorlds(); print_worldspecs(worldspecs, dstream); return 0; } // Print startup message infostream<<PROJECT_NAME<< " "<<_("with")<<" SER_FMT_VER_HIGHEST_READ="<<(int)SER_FMT_VER_HIGHEST_READ <<", "<<minetest_build_info <<std::endl; /* Basic initialization */ // Initialize default settings set_default_settings(g_settings); // Initialize sockets sockets_init(); atexit(sockets_cleanup); /* Read config file */ // Path of configuration file in use g_settings_path = ""; if(cmd_args.exists("config")) { bool r = g_settings->readConfigFile(cmd_args.get("config").c_str()); if(r == false) { errorstream<<"Could not read configuration from \"" <<cmd_args.get("config")<<"\""<<std::endl; return 1; } g_settings_path = cmd_args.get("config"); } else { std::vector<std::string> filenames; filenames.push_back(porting::path_user + DIR_DELIM + "minetest.conf"); // Legacy configuration file location filenames.push_back(porting::path_user + DIR_DELIM + ".." + DIR_DELIM + "minetest.conf"); #if RUN_IN_PLACE // Try also from a lower level (to aid having the same configuration // for many RUN_IN_PLACE installs) filenames.push_back(porting::path_user + DIR_DELIM + ".." + DIR_DELIM + ".." + DIR_DELIM + "minetest.conf"); #endif for(u32 i=0; i<filenames.size(); i++) { bool r = g_settings->readConfigFile(filenames[i].c_str()); if(r) { g_settings_path = filenames[i]; break; } } // If no path found, use the first one (menu creates the file) if(g_settings_path == "") g_settings_path = filenames[0]; } // Initialize debug streams #define DEBUGFILE "debug.txt" #if RUN_IN_PLACE std::string logfile = DEBUGFILE; #else std::string logfile = porting::path_user+DIR_DELIM+DEBUGFILE; #endif if(cmd_args.exists("logfile")) logfile = cmd_args.get("logfile"); log_remove_output(&main_dstream_no_stderr_log_out); int loglevel = g_settings->getS32("debug_log_level"); if (loglevel == 0) //no logging logfile = ""; else if (loglevel > 0 && loglevel <= LMT_NUM_VALUES) log_add_output_maxlev(&main_dstream_no_stderr_log_out, (LogMessageLevel)(loglevel - 1)); if(logfile != "") debugstreams_init(false, logfile.c_str()); else debugstreams_init(false, NULL); infostream<<"logfile = "<<logfile<<std::endl; // Initialize random seed srand(time(0)); mysrand(time(0)); // Initialize HTTP fetcher httpfetch_init(g_settings->getS32("curl_parallel_limit")); /* Run unit tests */ if((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false) || cmd_args.getFlag("enable-unittests") == true) { run_tests(); } #ifdef _MSC_VER init_gettext((porting::path_share + DIR_DELIM + "locale").c_str(),g_settings->get("language"),argc,argv); #else init_gettext((porting::path_share + DIR_DELIM + "locale").c_str(),g_settings->get("language")); #endif /* Game parameters */ // Port u16 port = 30000; if(cmd_args.exists("port")) port = cmd_args.getU16("port"); else if(g_settings->exists("port")) port = g_settings->getU16("port"); if(port == 0) port = 30000; // World directory std::string commanded_world = ""; if(cmd_args.exists("world")) commanded_world = cmd_args.get("world"); else if(cmd_args.exists("map-dir")) commanded_world = cmd_args.get("map-dir"); else if(cmd_args.exists("nonopt0")) // First nameless argument commanded_world = cmd_args.get("nonopt0"); else if(g_settings->exists("map-dir")) commanded_world = g_settings->get("map-dir"); // World name std::string commanded_worldname = ""; if(cmd_args.exists("worldname")) commanded_worldname = cmd_args.get("worldname"); // Strip world.mt from commanded_world { std::string worldmt = "world.mt"; if(commanded_world.size() > worldmt.size() && commanded_world.substr(commanded_world.size()-worldmt.size()) == worldmt){ dstream<<_("Supplied world.mt file - stripping it off.")<<std::endl; commanded_world = commanded_world.substr( 0, commanded_world.size()-worldmt.size()); } } // If a world name was specified, convert it to a path if(commanded_worldname != ""){ // Get information about available worlds std::vector<WorldSpec> worldspecs = getAvailableWorlds(); bool found = false; for(u32 i=0; i<worldspecs.size(); i++){ std::string name = worldspecs[i].name; if(name == commanded_worldname){ if(commanded_world != ""){ dstream<<_("--worldname takes precedence over previously " "selected world.")<<std::endl; } commanded_world = worldspecs[i].path; found = true; break; } } if(!found){ dstream<<_("World")<<" '"<<commanded_worldname<<_("' not " "available. Available worlds:")<<std::endl; print_worldspecs(worldspecs, dstream); return 1; } } // Gamespec SubgameSpec commanded_gamespec; if(cmd_args.exists("gameid")){ std::string gameid = cmd_args.get("gameid"); commanded_gamespec = findSubgame(gameid); if(!commanded_gamespec.isValid()){ errorstream<<"Game \""<<gameid<<"\" not found"<<std::endl; return 1; } } /* Run dedicated server if asked to or no other option */ #ifdef SERVER bool run_dedicated_server = true; #else bool run_dedicated_server = cmd_args.getFlag("server"); #endif g_settings->set("server_dedicated", run_dedicated_server ? "true" : "false"); if(run_dedicated_server) { DSTACK("Dedicated server branch"); // Create time getter if built with Irrlicht #ifndef SERVER g_timegetter = new SimpleTimeGetter(); #endif // World directory std::string world_path; verbosestream<<_("Determining world path")<<std::endl; bool is_legacy_world = false; // If a world was commanded, use it if(commanded_world != ""){ world_path = commanded_world; infostream<<"Using commanded world path ["<<world_path<<"]" <<std::endl; } // No world was specified; try to select it automatically else { // Get information about available worlds std::vector<WorldSpec> worldspecs = getAvailableWorlds(); // If a world name was specified, select it if(commanded_worldname != ""){ world_path = ""; for(u32 i=0; i<worldspecs.size(); i++){ std::string name = worldspecs[i].name; if(name == commanded_worldname){ world_path = worldspecs[i].path; break; } } if(world_path == ""){ dstream<<_("World")<<" '"<<commanded_worldname<<"' "<<_("not " "available. Available worlds:")<<std::endl; print_worldspecs(worldspecs, dstream); return 1; } } // If there is only a single world, use it if(worldspecs.size() == 1){ world_path = worldspecs[0].path; dstream<<_("Automatically selecting world at")<<" [" <<world_path<<"]"<<std::endl; // If there are multiple worlds, list them } else if(worldspecs.size() > 1){ dstream<<_("Multiple worlds are available.")<<std::endl; dstream<<_("Please select one using --worldname <name>" " or --world <path>")<<std::endl; print_worldspecs(worldspecs, dstream); return 1; // If there are no worlds, automatically create a new one } else { // This is the ultimate default world path world_path = porting::path_user + DIR_DELIM + "worlds" + DIR_DELIM + "world"; infostream<<"Creating default world at [" <<world_path<<"]"<<std::endl; } } if(world_path == ""){ errorstream<<"No world path specified or found."<<std::endl; return 1; } verbosestream<<_("Using world path")<<" ["<<world_path<<"]"<<std::endl; // We need a gamespec. SubgameSpec gamespec; verbosestream<<_("Determining gameid/gamespec")<<std::endl; // If world doesn't exist if(!getWorldExists(world_path)) { // Try to take gamespec from command line if(commanded_gamespec.isValid()){ gamespec = commanded_gamespec; infostream<<"Using commanded gameid ["<<gamespec.id<<"]"<<std::endl; } // Otherwise we will be using "minetest" else{ gamespec = findSubgame(g_settings->get("default_game")); infostream<<"Using default gameid ["<<gamespec.id<<"]"<<std::endl; } } // World exists else { std::string world_gameid = getWorldGameId(world_path, is_legacy_world); // If commanded to use a gameid, do so if(commanded_gamespec.isValid()){ gamespec = commanded_gamespec; if(commanded_gamespec.id != world_gameid){ errorstream<<"WARNING: Using commanded gameid [" <<gamespec.id<<"]"<<" instead of world gameid [" <<world_gameid<<"]"<<std::endl; } } else{ // If world contains an embedded game, use it; // Otherwise find world from local system. gamespec = findWorldSubgame(world_path); infostream<<"Using world gameid ["<<gamespec.id<<"]"<<std::endl; } } if(!gamespec.isValid()){ errorstream<<"Subgame ["<<gamespec.id<<"] could not be found." <<std::endl; return 1; } verbosestream<<_("Using gameid")<<" ["<<gamespec.id<<"]"<<std::endl; // Bind address std::string bind_str = g_settings->get("bind_address"); Address bind_addr(0,0,0,0, port); if (g_settings->getBool("ipv6_server")) { bind_addr.setAddress((IPv6AddressBytes*) NULL); } try { bind_addr.Resolve(bind_str.c_str()); } catch (ResolveError &e) { infostream << "Resolving bind address \"" << bind_str << "\" failed: " << e.what() << " -- Listening on all addresses." << std::endl; } if(bind_addr.isIPv6() && !g_settings->getBool("enable_ipv6")) { errorstream << "Unable to listen on " << bind_addr.serializeString() << L" because IPv6 is disabled" << std::endl; return 1; } // Create server Server server(world_path, gamespec, false, bind_addr.isIPv6()); // Database migration if (cmd_args.exists("migrate")) { std::string migrate_to = cmd_args.get("migrate"); Settings world_mt; bool success = world_mt.readConfigFile((world_path + DIR_DELIM + "world.mt").c_str()); if (!success) { errorstream << "Cannot read world.mt" << std::endl; return 1; } if (!world_mt.exists("backend")) { errorstream << "Please specify your current backend in world.mt file:" << std::endl << " backend = {sqlite3|leveldb|redis|dummy}" << std::endl; return 1; } std::string backend = world_mt.get("backend"); Database *new_db; if (backend == migrate_to) { errorstream << "Cannot migrate: new backend is same as the old one" << std::endl; return 1; } if (migrate_to == "sqlite3") new_db = new Database_SQLite3(&(ServerMap&)server.getMap(), world_path); #if USE_LEVELDB else if (migrate_to == "leveldb") new_db = new Database_LevelDB(&(ServerMap&)server.getMap(), world_path); #endif #if USE_REDIS else if (migrate_to == "redis") new_db = new Database_Redis(&(ServerMap&)server.getMap(), world_path); #endif else { errorstream << "Migration to " << migrate_to << " is not supported" << std::endl; return 1; } std::list<v3s16> blocks; ServerMap &old_map = ((ServerMap&)server.getMap()); old_map.listAllLoadableBlocks(blocks); int count = 0; new_db->beginSave(); for (std::list<v3s16>::iterator i = blocks.begin(); i != blocks.end(); ++i) { MapBlock *block = old_map.loadBlock(*i); new_db->saveBlock(block); MapSector *sector = old_map.getSectorNoGenerate(v2s16(i->X, i->Z)); sector->deleteBlock(block); ++count; if (count % 500 == 0) actionstream << "Migrated " << count << " blocks " << (100.0 * count / blocks.size()) << "% completed" << std::endl; } new_db->endSave(); delete new_db; actionstream << "Successfully migrated " << count << " blocks" << std::endl; world_mt.set("backend", migrate_to); if(!world_mt.updateConfigFile((world_path + DIR_DELIM + "world.mt").c_str())) errorstream<<"Failed to update world.mt!"<<std::endl; else actionstream<<"world.mt updated"<<std::endl; return 0; } server.start(bind_addr); // Run server dedicated_server_loop(server, kill); return 0; } #ifndef SERVER // Exclude from dedicated server build /* More parameters */ std::string address = g_settings->get("address"); if(commanded_world != "") address = ""; else if(cmd_args.exists("address")) address = cmd_args.get("address"); std::string playername = g_settings->get("name"); if(cmd_args.exists("name")) playername = cmd_args.get("name"); bool skip_main_menu = cmd_args.getFlag("go"); /* Device initialization */ // Resolution selection bool fullscreen = g_settings->getBool("fullscreen"); u16 screenW = g_settings->getU16("screenW"); u16 screenH = g_settings->getU16("screenH"); // bpp, fsaa, vsync bool vsync = g_settings->getBool("vsync"); u16 bits = g_settings->getU16("fullscreen_bpp"); u16 fsaa = g_settings->getU16("fsaa"); // Determine driver video::E_DRIVER_TYPE driverType; std::string driverstring = g_settings->get("video_driver"); if(driverstring == "null") driverType = video::EDT_NULL; else if(driverstring == "software") driverType = video::EDT_SOFTWARE; else if(driverstring == "burningsvideo") driverType = video::EDT_BURNINGSVIDEO; else if(driverstring == "direct3d8") driverType = video::EDT_DIRECT3D8; else if(driverstring == "direct3d9") driverType = video::EDT_DIRECT3D9; else if(driverstring == "opengl") driverType = video::EDT_OPENGL; #ifdef _IRR_COMPILE_WITH_OGLES1_ else if(driverstring == "ogles1") driverType = video::EDT_OGLES1; #endif #ifdef _IRR_COMPILE_WITH_OGLES2_ else if(driverstring == "ogles2") driverType = video::EDT_OGLES2; #endif else { errorstream<<"WARNING: Invalid video_driver specified; defaulting " "to opengl"<<std::endl; driverType = video::EDT_OPENGL; } /* List video modes if requested */ MyEventReceiver receiver; if(cmd_args.getFlag("videomodes")){ IrrlichtDevice *nulldevice; SIrrlichtCreationParameters params = SIrrlichtCreationParameters(); params.DriverType = video::EDT_NULL; params.WindowSize = core::dimension2d<u32>(640, 480); params.Bits = 24; params.AntiAlias = fsaa; params.Fullscreen = false; params.Stencilbuffer = false; params.Vsync = vsync; params.EventReceiver = &receiver; params.HighPrecisionFPU = g_settings->getBool("high_precision_fpu"); nulldevice = createDeviceEx(params); if(nulldevice == 0) return 1; dstream<<_("Available video modes (WxHxD):")<<std::endl; video::IVideoModeList *videomode_list = nulldevice->getVideoModeList(); if(videomode_list == 0){ nulldevice->drop(); return 1; } s32 videomode_count = videomode_list->getVideoModeCount(); core::dimension2d<u32> videomode_res; s32 videomode_depth; for (s32 i = 0; i < videomode_count; ++i){ videomode_res = videomode_list->getVideoModeResolution(i); videomode_depth = videomode_list->getVideoModeDepth(i); dstream<<videomode_res.Width<<"x"<<videomode_res.Height <<"x"<<videomode_depth<<std::endl; } dstream<<_("Active video mode (WxHxD):")<<std::endl; videomode_res = videomode_list->getDesktopResolution(); videomode_depth = videomode_list->getDesktopDepth(); dstream<<videomode_res.Width<<"x"<<videomode_res.Height <<"x"<<videomode_depth<<std::endl; nulldevice->drop(); return 0; } /* Create device and exit if creation failed */ IrrlichtDevice *device; SIrrlichtCreationParameters params = SIrrlichtCreationParameters(); params.DriverType = driverType; params.WindowSize = core::dimension2d<u32>(screenW, screenH); params.Bits = bits; params.AntiAlias = fsaa; params.Fullscreen = fullscreen; params.Stencilbuffer = false; params.Vsync = vsync; params.EventReceiver = &receiver; params.HighPrecisionFPU = g_settings->getBool("high_precision_fpu"); device = createDeviceEx(params); if (device == 0) return 1; // could not create selected driver. /* Continue initialization */ video::IVideoDriver* driver = device->getVideoDriver(); /* This changes the minimum allowed number of vertices in a VBO. Default is 500. */ //driver->setMinHardwareBufferVertexCount(50); // Create time getter g_timegetter = new IrrlichtTimeGetter(device); // Create game callback for menus g_gamecallback = new MainGameCallback(device); /* Speed tests (done after irrlicht is loaded to get timer) */ if(cmd_args.getFlag("speedtests")) { dstream<<"Running speed tests"<<std::endl; SpeedTests(); device->drop(); return 0; } device->setResizable(true); bool random_input = g_settings->getBool("random_input") || cmd_args.getFlag("random-input"); InputHandler *input = NULL; if(random_input) { input = new RandomInputHandler(); } else { input = new RealInputHandler(device, &receiver); } scene::ISceneManager* smgr = device->getSceneManager(); guienv = device->getGUIEnvironment(); gui::IGUISkin* skin = guienv->getSkin(); std::string font_path = g_settings->get("font_path"); gui::IGUIFont *font; #if USE_FREETYPE bool use_freetype = g_settings->getBool("freetype"); if (use_freetype) { std::string fallback; if (is_yes(gettext("needs_fallback_font"))) fallback = "fallback_"; u16 font_size = g_settings->getU16(fallback + "font_size"); font_path = g_settings->get(fallback + "font_path"); u32 font_shadow = g_settings->getU16(fallback + "font_shadow"); u32 font_shadow_alpha = g_settings->getU16(fallback + "font_shadow_alpha"); font = gui::CGUITTFont::createTTFont(guienv, font_path.c_str(), font_size, true, true, font_shadow, font_shadow_alpha); } else { font = guienv->getFont(font_path.c_str()); } #else font = guienv->getFont(font_path.c_str()); #endif if(font) skin->setFont(font); else errorstream<<"WARNING: Font file was not found." " Using default font."<<std::endl; // If font was not found, this will get us one font = skin->getFont(); assert(font); u32 text_height = font->getDimension(L"Hello, world!").Height; infostream<<"text_height="<<text_height<<std::endl; //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0)); skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255)); //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0)); //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0)); skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0)); skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0)); skin->setColor(gui::EGDC_HIGH_LIGHT, video::SColor(255,70,100,50)); skin->setColor(gui::EGDC_HIGH_LIGHT_TEXT, video::SColor(255,255,255,255)); #if (IRRLICHT_VERSION_MAJOR >= 1 && IRRLICHT_VERSION_MINOR >= 8) || IRRLICHT_VERSION_MAJOR >= 2 // Irrlicht 1.8 input colours skin->setColor(gui::EGDC_EDITABLE, video::SColor(255,128,128,128)); skin->setColor(gui::EGDC_FOCUSED_EDITABLE, video::SColor(255,96,134,49)); #endif // Create the menu clouds if (!g_menucloudsmgr) g_menucloudsmgr = smgr->createNewSceneManager(); if (!g_menuclouds) g_menuclouds = new Clouds(g_menucloudsmgr->getRootSceneNode(), g_menucloudsmgr, -1, rand(), 100); g_menuclouds->update(v2f(0, 0), video::SColor(255,200,200,255)); scene::ICameraSceneNode* camera; camera = g_menucloudsmgr->addCameraSceneNode(0, v3f(0,0,0), v3f(0, 60, 100)); camera->setFarValue(10000); /* GUI stuff */ ChatBackend chat_backend; /* If an error occurs, this is set to something and the menu-game loop is restarted. It is then displayed before the menu. */ std::wstring error_message = L""; // The password entered during the menu screen, std::string password; bool first_loop = true; /* Menu-game loop */ while(device->run() && kill == false) { // Set the window caption wchar_t* text = wgettext("Main Menu"); device->setWindowCaption((std::wstring(L"Minetest [")+text+L"]").c_str()); delete[] text; // This is used for catching disconnects try { /* Clear everything from the GUIEnvironment */ guienv->clear(); /* We need some kind of a root node to be able to add custom gui elements directly on the screen. Otherwise they won't be automatically drawn. */ guiroot = guienv->addStaticText(L"", core::rect<s32>(0, 0, 10000, 10000)); SubgameSpec gamespec; WorldSpec worldspec; bool simple_singleplayer_mode = false; // These are set up based on the menu and other things std::string current_playername = "inv£lid"; std::string current_password = ""; std::string current_address = "does-not-exist"; int current_port = 0; /* Out-of-game menu loop. Loop quits when menu returns proper parameters. */ while(kill == false) { // If skip_main_menu, only go through here once if(skip_main_menu && !first_loop){ kill = true; break; } first_loop = false; // Cursor can be non-visible when coming from the game device->getCursorControl()->setVisible(true); // Some stuff are left to scene manager when coming from the game // (map at least?) smgr->clear(); // Initialize menu data MainMenuData menudata; menudata.address = address; menudata.name = playername; menudata.port = itos(port); menudata.errormessage = wide_to_narrow(error_message); error_message = L""; if(cmd_args.exists("password")) menudata.password = cmd_args.get("password"); driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, g_settings->getBool("mip_map")); menudata.enable_public = g_settings->getBool("server_announce"); std::vector<WorldSpec> worldspecs = getAvailableWorlds(); // If a world was commanded, append and select it if(commanded_world != ""){ std::string gameid = getWorldGameId(commanded_world, true); std::string name = _("[--world parameter]"); if(gameid == ""){ gameid = g_settings->get("default_game"); name += " [new]"; } //TODO find within worldspecs and set config } if(skip_main_menu == false) { video::IVideoDriver* driver = device->getVideoDriver(); infostream<<"Waiting for other menus"<<std::endl; while(device->run() && kill == false) { if(noMenuActive()) break; driver->beginScene(true, true, video::SColor(255,128,128,128)); guienv->drawAll(); driver->endScene(); // On some computers framerate doesn't seem to be // automatically limited sleep_ms(25); } infostream<<"Waited for other menus"<<std::endl; GUIEngine* temp = new GUIEngine(device, guiroot, &g_menumgr,smgr,&menudata,kill); delete temp; //once finished you'll never end up here smgr->clear(); } if(menudata.errormessage != ""){ error_message = narrow_to_wide(menudata.errormessage); continue; } //update worldspecs (necessary as new world may have been created) worldspecs = getAvailableWorlds(); if (menudata.name == "") menudata.name = std::string("Guest") + itos(myrand_range(1000,9999)); else playername = menudata.name; password = translatePassword(playername, narrow_to_wide(menudata.password)); //infostream<<"Main: password hash: '"<<password<<"'"<<std::endl; address = menudata.address; int newport = stoi(menudata.port); if(newport != 0) port = newport; simple_singleplayer_mode = menudata.simple_singleplayer_mode; // Save settings g_settings->set("name", playername); if((menudata.selected_world >= 0) && (menudata.selected_world < (int)worldspecs.size())) g_settings->set("selected_world_path", worldspecs[menudata.selected_world].path); // Break out of menu-game loop to shut down cleanly if(device->run() == false || kill == true) break; current_playername = playername; current_password = password; current_address = address; current_port = port; // If using simple singleplayer mode, override if(simple_singleplayer_mode){ current_playername = "singleplayer"; current_password = ""; current_address = ""; current_port = myrand_range(49152, 65535); } else if (address != "") { ServerListSpec server; server["name"] = menudata.servername; server["address"] = menudata.address; server["port"] = menudata.port; server["description"] = menudata.serverdescription; ServerList::insert(server); } // Set world path to selected one if ((menudata.selected_world >= 0) && (menudata.selected_world < (int)worldspecs.size())) { worldspec = worldspecs[menudata.selected_world]; infostream<<"Selected world: "<<worldspec.name <<" ["<<worldspec.path<<"]"<<std::endl; } // If local game if(current_address == "") { if(menudata.selected_world == -1){ error_message = wgettext("No world selected and no address " "provided. Nothing to do."); errorstream<<wide_to_narrow(error_message)<<std::endl; continue; } // Load gamespec for required game gamespec = findWorldSubgame(worldspec.path); if(!gamespec.isValid() && !commanded_gamespec.isValid()){ error_message = wgettext("Could not find or load game \"") + narrow_to_wide(worldspec.gameid) + L"\""; errorstream<<wide_to_narrow(error_message)<<std::endl; continue; } if(commanded_gamespec.isValid() && commanded_gamespec.id != worldspec.gameid){ errorstream<<"WARNING: Overriding gamespec from \"" <<worldspec.gameid<<"\" to \"" <<commanded_gamespec.id<<"\""<<std::endl; gamespec = commanded_gamespec; } if(!gamespec.isValid()){ error_message = wgettext("Invalid gamespec."); error_message += L" (world_gameid=" +narrow_to_wide(worldspec.gameid)+L")"; errorstream<<wide_to_narrow(error_message)<<std::endl; continue; } } // Continue to game break; } // Break out of menu-game loop to shut down cleanly if(device->run() == false || kill == true) { if(g_settings_path != "") { g_settings->updateConfigFile( g_settings_path.c_str()); } break; } /* Run game */ the_game( kill, random_input, input, device, font, worldspec.path, current_playername, current_password, current_address, current_port, error_message, chat_backend, gamespec, simple_singleplayer_mode ); smgr->clear(); } //try catch(con::PeerNotFoundException &e) { error_message = wgettext("Connection error (timed out?)"); errorstream<<wide_to_narrow(error_message)<<std::endl; } #ifdef NDEBUG catch(std::exception &e) { std::string narrow_message = "Some exception: \""; narrow_message += e.what(); narrow_message += "\""; errorstream<<narrow_message<<std::endl; error_message = narrow_to_wide(narrow_message); } #endif // If no main menu, show error and exit if(skip_main_menu) { if(error_message != L""){ verbosestream<<"error_message = " <<wide_to_narrow(error_message)<<std::endl; retval = 1; } break; } } // Menu-game loop g_menuclouds->drop(); g_menucloudsmgr->drop(); delete input; /* In the end, delete the Irrlicht device. */ device->drop(); #if USE_FREETYPE if (use_freetype) font->drop(); #endif #endif // !SERVER // Update configuration file if(g_settings_path != "") g_settings->updateConfigFile(g_settings_path.c_str()); // Print modified quicktune values { bool header_printed = false; std::vector<std::string> names = getQuicktuneNames(); for(u32 i=0; i<names.size(); i++){ QuicktuneValue val = getQuicktuneValue(names[i]); if(!val.modified) continue; if(!header_printed){ dstream<<"Modified quicktune values:"<<std::endl; header_printed = true; } dstream<<names[i]<<" = "<<val.getString()<<std::endl; } } // Stop httpfetch thread (if started) httpfetch_cleanup(); END_DEBUG_EXCEPTION_HANDLER(errorstream) debugstreams_deinit(); return retval; }
/* SectorPropsPanel::applyChanges * Apply values to opened objects *******************************************************************/ void SectorPropsPanel::applyChanges() { for (unsigned a = 0; a < objects.size(); a++) { MapSector* sector = (MapSector*)objects[a]; // Special if (cb_override_special->GetValue()) sector->setIntProperty("special", panel_special->getSelectedSpecial(theMapEditor->currentMapDesc().format)); // Floor texture if (!fcb_floor->GetValue().IsEmpty()) sector->setStringProperty("texturefloor", fcb_floor->GetValue()); // Ceiling texture if (!fcb_ceiling->GetValue().IsEmpty()) sector->setStringProperty("textureceiling", fcb_ceiling->GetValue()); // Floor height if (!text_height_floor->GetValue().IsEmpty()) sector->setIntProperty("heightfloor", text_height_floor->getNumber(sector->getFloorHeight())); // Ceiling height if (!text_height_ceiling->GetValue().IsEmpty()) sector->setIntProperty("heightceiling", text_height_ceiling->getNumber(sector->getCeilingHeight())); // Light level if (!text_light->GetValue().IsEmpty()) sector->setIntProperty("lightlevel", text_light->getNumber(sector->getLightLevel())); // Tag if (!text_tag->GetValue().IsEmpty()) sector->setIntProperty("id", text_tag->getNumber(sector->getTag())); } if (mopp_all_props) mopp_all_props->applyChanges(); }
void ClientMap::updateDrawList(video::IVideoDriver* driver) { ScopeProfiler sp(g_profiler, "CM::updateDrawList()", SPT_AVG); g_profiler->add("CM::updateDrawList() count", 1); INodeDefManager *nodemgr = m_gamedef->ndef(); for (std::map<v3s16, MapBlock*>::iterator i = m_drawlist.begin(); i != m_drawlist.end(); ++i) { MapBlock *block = i->second; block->refDrop(); } m_drawlist.clear(); v3f camera_position = m_camera_position; v3f camera_direction = m_camera_direction; f32 camera_fov = m_camera_fov; // Use a higher fov to accomodate faster camera movements. // Blocks are cropped better when they are drawn. // Or maybe they aren't? Well whatever. camera_fov *= 1.2; v3s16 cam_pos_nodes = floatToInt(camera_position, BS); v3s16 p_blocks_min; v3s16 p_blocks_max; getBlocksInViewRange(cam_pos_nodes, &p_blocks_min, &p_blocks_max); // Number of blocks in rendering range u32 blocks_in_range = 0; // Number of blocks occlusion culled u32 blocks_occlusion_culled = 0; // Number of blocks in rendering range but don't have a mesh u32 blocks_in_range_without_mesh = 0; // Blocks that had mesh that would have been drawn according to // rendering range (if max blocks limit didn't kick in) u32 blocks_would_have_drawn = 0; // Blocks that were drawn and had a mesh u32 blocks_drawn = 0; // Blocks which had a corresponding meshbuffer for this pass //u32 blocks_had_pass_meshbuf = 0; // Blocks from which stuff was actually drawn //u32 blocks_without_stuff = 0; // Distance to farthest drawn block float farthest_drawn = 0; for (std::map<v2s16, MapSector*>::iterator si = m_sectors.begin(); si != m_sectors.end(); ++si) { MapSector *sector = si->second; v2s16 sp = sector->getPos(); if (m_control.range_all == false) { if (sp.X < p_blocks_min.X || sp.X > p_blocks_max.X || sp.Y < p_blocks_min.Z || sp.Y > p_blocks_max.Z) continue; } MapBlockVect sectorblocks; sector->getBlocks(sectorblocks); /* Loop through blocks in sector */ u32 sector_blocks_drawn = 0; for (MapBlockVect::iterator i = sectorblocks.begin(); i != sectorblocks.end(); ++i) { MapBlock *block = *i; /* Compare block position to camera position, skip if not seen on display */ if (block->mesh != NULL) block->mesh->updateCameraOffset(m_camera_offset); float range = 100000 * BS; if (m_control.range_all == false) range = m_control.wanted_range * BS; float d = 0.0; if (!isBlockInSight(block->getPos(), camera_position, camera_direction, camera_fov, range, &d)) continue; // This is ugly (spherical distance limit?) /*if(m_control.range_all == false && d - 0.5*BS*MAP_BLOCKSIZE > range) continue;*/ blocks_in_range++; /* Ignore if mesh doesn't exist */ { //MutexAutoLock lock(block->mesh_mutex); if (block->mesh == NULL) { blocks_in_range_without_mesh++; continue; } } /* Occlusion culling */ // No occlusion culling when free_move is on and camera is // inside ground bool occlusion_culling_enabled = true; if (g_settings->getBool("free_move")) { MapNode n = getNodeNoEx(cam_pos_nodes); if (n.getContent() == CONTENT_IGNORE || nodemgr->get(n).solidness == 2) occlusion_culling_enabled = false; } v3s16 cpn = block->getPos() * MAP_BLOCKSIZE; cpn += v3s16(MAP_BLOCKSIZE / 2, MAP_BLOCKSIZE / 2, MAP_BLOCKSIZE / 2); float step = BS * 1; float stepfac = 1.1; float startoff = BS * 1; // The occlusion search of 'isOccluded()' must stop short of the target // point by distance 'endoff' (end offset) to not enter the target mapblock. // For the 8 mapblock corners 'endoff' must therefore be the maximum diagonal // of a mapblock, because we must consider all view angles. // sqrt(1^2 + 1^2 + 1^2) = 1.732 float endoff = -BS * MAP_BLOCKSIZE * 1.732050807569; v3s16 spn = cam_pos_nodes; s16 bs2 = MAP_BLOCKSIZE / 2 + 1; // to reduce the likelihood of falsely occluded blocks // require at least two solid blocks // this is a HACK, we should think of a more precise algorithm u32 needed_count = 2; if (occlusion_culling_enabled && // For the central point of the mapblock 'endoff' can be halved isOccluded(this, spn, cpn, step, stepfac, startoff, endoff / 2.0f, needed_count, nodemgr) && isOccluded(this, spn, cpn + v3s16(bs2,bs2,bs2), step, stepfac, startoff, endoff, needed_count, nodemgr) && isOccluded(this, spn, cpn + v3s16(bs2,bs2,-bs2), step, stepfac, startoff, endoff, needed_count, nodemgr) && isOccluded(this, spn, cpn + v3s16(bs2,-bs2,bs2), step, stepfac, startoff, endoff, needed_count, nodemgr) && isOccluded(this, spn, cpn + v3s16(bs2,-bs2,-bs2), step, stepfac, startoff, endoff, needed_count, nodemgr) && isOccluded(this, spn, cpn + v3s16(-bs2,bs2,bs2), step, stepfac, startoff, endoff, needed_count, nodemgr) && isOccluded(this, spn, cpn + v3s16(-bs2,bs2,-bs2), step, stepfac, startoff, endoff, needed_count, nodemgr) && isOccluded(this, spn, cpn + v3s16(-bs2,-bs2,bs2), step, stepfac, startoff, endoff, needed_count, nodemgr) && isOccluded(this, spn, cpn + v3s16(-bs2,-bs2,-bs2), step, stepfac, startoff, endoff, needed_count, nodemgr)) { blocks_occlusion_culled++; continue; } // This block is in range. Reset usage timer. block->resetUsageTimer(); // Limit block count in case of a sudden increase blocks_would_have_drawn++; if (blocks_drawn >= m_control.wanted_max_blocks && !m_control.range_all && d > m_control.wanted_range * BS) continue; // Add to set block->refGrab(); m_drawlist[block->getPos()] = block; sector_blocks_drawn++; blocks_drawn++; if (d / BS > farthest_drawn) farthest_drawn = d / BS; } // foreach sectorblocks if (sector_blocks_drawn != 0) m_last_drawn_sectors.insert(sp); } m_control.blocks_would_have_drawn = blocks_would_have_drawn; m_control.blocks_drawn = blocks_drawn; m_control.farthest_drawn = farthest_drawn; g_profiler->avg("CM: blocks in range", blocks_in_range); g_profiler->avg("CM: blocks occlusion culled", blocks_occlusion_culled); if (blocks_in_range != 0) g_profiler->avg("CM: blocks in range without mesh (frac)", (float)blocks_in_range_without_mesh / blocks_in_range); g_profiler->avg("CM: blocks drawn", blocks_drawn); g_profiler->avg("CM: farthest drawn", farthest_drawn); g_profiler->avg("CM: wanted max blocks", m_control.wanted_max_blocks); }
void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) { INodeDefManager *nodemgr = m_gamedef->ndef(); //m_dout<<DTIME<<"Rendering map..."<<std::endl; DSTACK(__FUNCTION_NAME); bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT; std::string prefix; if(pass == scene::ESNRP_SOLID) prefix = "CM: solid: "; else prefix = "CM: transparent: "; /* This is called two times per frame, reset on the non-transparent one */ if(pass == scene::ESNRP_SOLID) { m_last_drawn_sectors.clear(); } /* Get time for measuring timeout. Measuring time is very useful for long delays when the machine is swapping a lot. */ int time1 = time(0); /* Get animation parameters */ float animation_time = m_client->getAnimationTime(); int crack = m_client->getCrackLevel(); u32 daynight_ratio = m_client->getEnv().getDayNightRatio(); m_camera_mutex.Lock(); v3f camera_position = m_camera_position; v3f camera_direction = m_camera_direction; f32 camera_fov = m_camera_fov; m_camera_mutex.Unlock(); /* Get all blocks and draw all visible ones */ v3s16 cam_pos_nodes = floatToInt(camera_position, BS); v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1); v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d; v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d; // Take a fair amount as we will be dropping more out later // Umm... these additions are a bit strange but they are needed. v3s16 p_blocks_min( p_nodes_min.X / MAP_BLOCKSIZE - 3, p_nodes_min.Y / MAP_BLOCKSIZE - 3, p_nodes_min.Z / MAP_BLOCKSIZE - 3); v3s16 p_blocks_max( p_nodes_max.X / MAP_BLOCKSIZE + 1, p_nodes_max.Y / MAP_BLOCKSIZE + 1, p_nodes_max.Z / MAP_BLOCKSIZE + 1); u32 vertex_count = 0; u32 meshbuffer_count = 0; // For limiting number of mesh animations per frame u32 mesh_animate_count = 0; u32 mesh_animate_count_far = 0; // Number of blocks in rendering range u32 blocks_in_range = 0; // Number of blocks occlusion culled u32 blocks_occlusion_culled = 0; // Number of blocks in rendering range but don't have a mesh u32 blocks_in_range_without_mesh = 0; // Blocks that had mesh that would have been drawn according to // rendering range (if max blocks limit didn't kick in) u32 blocks_would_have_drawn = 0; // Blocks that were drawn and had a mesh u32 blocks_drawn = 0; // Blocks which had a corresponding meshbuffer for this pass u32 blocks_had_pass_meshbuf = 0; // Blocks from which stuff was actually drawn u32 blocks_without_stuff = 0; /* Collect a set of blocks for drawing */ core::map<v3s16, MapBlock*> drawset; { ScopeProfiler sp(g_profiler, prefix+"collecting blocks for drawing", SPT_AVG); for(core::map<v2s16, MapSector*>::Iterator si = m_sectors.getIterator(); si.atEnd() == false; si++) { MapSector *sector = si.getNode()->getValue(); v2s16 sp = sector->getPos(); if(m_control.range_all == false) { if(sp.X < p_blocks_min.X || sp.X > p_blocks_max.X || sp.Y < p_blocks_min.Z || sp.Y > p_blocks_max.Z) continue; } core::list< MapBlock * > sectorblocks; sector->getBlocks(sectorblocks); /* Loop through blocks in sector */ u32 sector_blocks_drawn = 0; core::list< MapBlock * >::Iterator i; for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++) { MapBlock *block = *i; /* Compare block position to camera position, skip if not seen on display */ float range = 100000 * BS; if(m_control.range_all == false) range = m_control.wanted_range * BS; float d = 0.0; if(isBlockInSight(block->getPos(), camera_position, camera_direction, camera_fov, range, &d) == false) { continue; } // This is ugly (spherical distance limit?) /*if(m_control.range_all == false && d - 0.5*BS*MAP_BLOCKSIZE > range) continue;*/ blocks_in_range++; /* Ignore if mesh doesn't exist */ { //JMutexAutoLock lock(block->mesh_mutex); if(block->mesh == NULL){ blocks_in_range_without_mesh++; continue; } } /* Occlusion culling */ // No occlusion culling when free_move is on and camera is // inside ground bool occlusion_culling_enabled = true; if(g_settings->getBool("free_move")){ MapNode n = getNodeNoEx(cam_pos_nodes); if(n.getContent() == CONTENT_IGNORE || nodemgr->get(n).solidness == 2) occlusion_culling_enabled = false; } v3s16 cpn = block->getPos() * MAP_BLOCKSIZE; cpn += v3s16(MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2); float step = BS*1; float stepfac = 1.1; float startoff = BS*1; float endoff = -BS*MAP_BLOCKSIZE*1.42*1.42; v3s16 spn = cam_pos_nodes + v3s16(0,0,0); s16 bs2 = MAP_BLOCKSIZE/2 + 1; u32 needed_count = 1; if( occlusion_culling_enabled && isOccluded(this, spn, cpn + v3s16(0,0,0), step, stepfac, startoff, endoff, needed_count, nodemgr) && isOccluded(this, spn, cpn + v3s16(bs2,bs2,bs2), step, stepfac, startoff, endoff, needed_count, nodemgr) && isOccluded(this, spn, cpn + v3s16(bs2,bs2,-bs2), step, stepfac, startoff, endoff, needed_count, nodemgr) && isOccluded(this, spn, cpn + v3s16(bs2,-bs2,bs2), step, stepfac, startoff, endoff, needed_count, nodemgr) && isOccluded(this, spn, cpn + v3s16(bs2,-bs2,-bs2), step, stepfac, startoff, endoff, needed_count, nodemgr) && isOccluded(this, spn, cpn + v3s16(-bs2,bs2,bs2), step, stepfac, startoff, endoff, needed_count, nodemgr) && isOccluded(this, spn, cpn + v3s16(-bs2,bs2,-bs2), step, stepfac, startoff, endoff, needed_count, nodemgr) && isOccluded(this, spn, cpn + v3s16(-bs2,-bs2,bs2), step, stepfac, startoff, endoff, needed_count, nodemgr) && isOccluded(this, spn, cpn + v3s16(-bs2,-bs2,-bs2), step, stepfac, startoff, endoff, needed_count, nodemgr) ) { blocks_occlusion_culled++; continue; } // This block is in range. Reset usage timer. block->resetUsageTimer(); // Limit block count in case of a sudden increase blocks_would_have_drawn++; if(blocks_drawn >= m_control.wanted_max_blocks && m_control.range_all == false && d > m_control.wanted_min_range * BS) continue; // Mesh animation { //JMutexAutoLock lock(block->mesh_mutex); MapBlockMesh *mapBlockMesh = block->mesh; // Pretty random but this should work somewhat nicely bool faraway = d >= BS*50; //bool faraway = d >= m_control.wanted_range * BS; if(mapBlockMesh->isAnimationForced() || !faraway || mesh_animate_count_far < (m_control.range_all ? 200 : 50)) { bool animated = mapBlockMesh->animate( faraway, animation_time, crack, daynight_ratio); if(animated) mesh_animate_count++; if(animated && faraway) mesh_animate_count_far++; } else { mapBlockMesh->decreaseAnimationForceTimer(); } } // Add to set drawset[block->getPos()] = block; sector_blocks_drawn++; blocks_drawn++; } // foreach sectorblocks if(sector_blocks_drawn != 0) m_last_drawn_sectors[sp] = true; } } // ScopeProfiler /* Draw the selected MapBlocks */ { ScopeProfiler sp(g_profiler, prefix+"drawing blocks", SPT_AVG); int timecheck_counter = 0; for(core::map<v3s16, MapBlock*>::Iterator i = drawset.getIterator(); i.atEnd() == false; i++) { { timecheck_counter++; if(timecheck_counter > 50) { timecheck_counter = 0; int time2 = time(0); if(time2 > time1 + 4) { infostream<<"ClientMap::renderMap(): " "Rendering takes ages, returning." <<std::endl; return; } } } MapBlock *block = i.getNode()->getValue(); /* Draw the faces of the block */ { //JMutexAutoLock lock(block->mesh_mutex); MapBlockMesh *mapBlockMesh = block->mesh; assert(mapBlockMesh); scene::SMesh *mesh = mapBlockMesh->getMesh(); assert(mesh); u32 c = mesh->getMeshBufferCount(); bool stuff_actually_drawn = false; for(u32 i=0; i<c; i++) { scene::IMeshBuffer *buf = mesh->getMeshBuffer(i); const video::SMaterial& material = buf->getMaterial(); video::IMaterialRenderer* rnd = driver->getMaterialRenderer(material.MaterialType); bool transparent = (rnd && rnd->isTransparent()); // Render transparent on transparent pass and likewise. if(transparent == is_transparent_pass) { if(buf->getVertexCount() == 0) errorstream<<"Block ["<<analyze_block(block) <<"] contains an empty meshbuf"<<std::endl; /* This *shouldn't* hurt too much because Irrlicht doesn't change opengl textures if the old material has the same texture. */ driver->setMaterial(buf->getMaterial()); driver->drawMeshBuffer(buf); vertex_count += buf->getVertexCount(); meshbuffer_count++; stuff_actually_drawn = true; } } if(stuff_actually_drawn) blocks_had_pass_meshbuf++; else blocks_without_stuff++; } } } // ScopeProfiler // Log only on solid pass because values are the same if(pass == scene::ESNRP_SOLID){ g_profiler->avg("CM: blocks in range", blocks_in_range); g_profiler->avg("CM: blocks occlusion culled", blocks_occlusion_culled); if(blocks_in_range != 0) g_profiler->avg("CM: blocks in range without mesh (frac)", (float)blocks_in_range_without_mesh/blocks_in_range); g_profiler->avg("CM: blocks drawn", blocks_drawn); g_profiler->avg("CM: animated meshes", mesh_animate_count); g_profiler->avg("CM: animated meshes (far)", mesh_animate_count_far); } g_profiler->avg(prefix+"vertices drawn", vertex_count); if(blocks_had_pass_meshbuf != 0) g_profiler->avg(prefix+"meshbuffers per block", (float)meshbuffer_count / (float)blocks_had_pass_meshbuf); if(blocks_drawn != 0) g_profiler->avg(prefix+"empty blocks (frac)", (float)blocks_without_stuff / blocks_drawn); m_control.blocks_drawn = blocks_drawn; m_control.blocks_would_have_drawn = blocks_would_have_drawn; /*infostream<<"renderMap(): is_transparent_pass="******", rendered "<<vertex_count<<" vertices."<<std::endl;*/ }
void ClientMap::updateDrawList(video::IVideoDriver* driver) { ScopeProfiler sp(g_profiler, "CM::updateDrawList()", SPT_AVG); g_profiler->add("CM::updateDrawList() count", 1); INodeDefManager *nodemgr = m_gamedef->ndef(); for(std::map<v3s16, MapBlock*>::iterator i = m_drawlist.begin(); i != m_drawlist.end(); ++i) { MapBlock *block = i->second; block->refDrop(); } m_drawlist.clear(); m_camera_mutex.Lock(); v3f camera_position = m_camera_position; v3f camera_direction = m_camera_direction; f32 camera_fov = m_camera_fov; m_camera_mutex.Unlock(); // Use a higher fov to accomodate faster camera movements. // Blocks are cropped better when they are drawn. // Or maybe they aren't? Well whatever. camera_fov *= 1.2; v3s16 cam_pos_nodes = floatToInt(camera_position, BS); v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1); v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d; v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d; // Take a fair amount as we will be dropping more out later // Umm... these additions are a bit strange but they are needed. v3s16 p_blocks_min( p_nodes_min.X / MAP_BLOCKSIZE - 3, p_nodes_min.Y / MAP_BLOCKSIZE - 3, p_nodes_min.Z / MAP_BLOCKSIZE - 3); v3s16 p_blocks_max( p_nodes_max.X / MAP_BLOCKSIZE + 1, p_nodes_max.Y / MAP_BLOCKSIZE + 1, p_nodes_max.Z / MAP_BLOCKSIZE + 1); // Number of blocks in rendering range u32 blocks_in_range = 0; // Number of blocks occlusion culled u32 blocks_occlusion_culled = 0; // Number of blocks in rendering range but don't have a mesh u32 blocks_in_range_without_mesh = 0; // Blocks that had mesh that would have been drawn according to // rendering range (if max blocks limit didn't kick in) u32 blocks_would_have_drawn = 0; // Blocks that were drawn and had a mesh u32 blocks_drawn = 0; // Blocks which had a corresponding meshbuffer for this pass //u32 blocks_had_pass_meshbuf = 0; // Blocks from which stuff was actually drawn //u32 blocks_without_stuff = 0; for(std::map<v2s16, MapSector*>::iterator si = m_sectors.begin(); si != m_sectors.end(); ++si) { MapSector *sector = si->second; v2s16 sp = sector->getPos(); if(m_control.range_all == false) { if(sp.X < p_blocks_min.X || sp.X > p_blocks_max.X || sp.Y < p_blocks_min.Z || sp.Y > p_blocks_max.Z) continue; } std::list< MapBlock * > sectorblocks; sector->getBlocks(sectorblocks); /* Loop through blocks in sector */ u32 sector_blocks_drawn = 0; std::list< MapBlock * >::iterator i; for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++) { MapBlock *block = *i; /* Compare block position to camera position, skip if not seen on display */ float range = 100000 * BS; if(m_control.range_all == false) range = m_control.wanted_range * BS; float d = 0.0; if(isBlockInSight(block->getPos(), camera_position, camera_direction, camera_fov, range, &d) == false) { continue; } // This is ugly (spherical distance limit?) /*if(m_control.range_all == false && d - 0.5*BS*MAP_BLOCKSIZE > range) continue;*/ blocks_in_range++; /* Ignore if mesh doesn't exist */ { //JMutexAutoLock lock(block->mesh_mutex); if(block->mesh == NULL){ blocks_in_range_without_mesh++; continue; } } /* Occlusion culling */ // No occlusion culling when free_move is on and camera is // inside ground bool occlusion_culling_enabled = true; if(g_settings->getBool("free_move")){ MapNode n = getNodeNoEx(cam_pos_nodes); if(n.getContent() == CONTENT_IGNORE || nodemgr->get(n).solidness == 2) occlusion_culling_enabled = false; } v3s16 cpn = block->getPos() * MAP_BLOCKSIZE; cpn += v3s16(MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2); float step = BS*1; float stepfac = 1.1; float startoff = BS*1; float endoff = -BS*MAP_BLOCKSIZE*1.42*1.42; v3s16 spn = cam_pos_nodes + v3s16(0,0,0); s16 bs2 = MAP_BLOCKSIZE/2 + 1; u32 needed_count = 1; if( occlusion_culling_enabled && isOccluded(this, spn, cpn + v3s16(0,0,0), step, stepfac, startoff, endoff, needed_count, nodemgr) && isOccluded(this, spn, cpn + v3s16(bs2,bs2,bs2), step, stepfac, startoff, endoff, needed_count, nodemgr) && isOccluded(this, spn, cpn + v3s16(bs2,bs2,-bs2), step, stepfac, startoff, endoff, needed_count, nodemgr) && isOccluded(this, spn, cpn + v3s16(bs2,-bs2,bs2), step, stepfac, startoff, endoff, needed_count, nodemgr) && isOccluded(this, spn, cpn + v3s16(bs2,-bs2,-bs2), step, stepfac, startoff, endoff, needed_count, nodemgr) && isOccluded(this, spn, cpn + v3s16(-bs2,bs2,bs2), step, stepfac, startoff, endoff, needed_count, nodemgr) && isOccluded(this, spn, cpn + v3s16(-bs2,bs2,-bs2), step, stepfac, startoff, endoff, needed_count, nodemgr) && isOccluded(this, spn, cpn + v3s16(-bs2,-bs2,bs2), step, stepfac, startoff, endoff, needed_count, nodemgr) && isOccluded(this, spn, cpn + v3s16(-bs2,-bs2,-bs2), step, stepfac, startoff, endoff, needed_count, nodemgr) ) { blocks_occlusion_culled++; continue; } // This block is in range. Reset usage timer. block->resetUsageTimer(); // Limit block count in case of a sudden increase blocks_would_have_drawn++; if(blocks_drawn >= m_control.wanted_max_blocks && m_control.range_all == false && d > m_control.wanted_min_range * BS) continue; // Add to set block->refGrab(); m_drawlist[block->getPos()] = block; sector_blocks_drawn++; blocks_drawn++; } // foreach sectorblocks if(sector_blocks_drawn != 0) m_last_drawn_sectors.insert(sp); } m_control.blocks_would_have_drawn = blocks_would_have_drawn; m_control.blocks_drawn = blocks_drawn; g_profiler->avg("CM: blocks in range", blocks_in_range); g_profiler->avg("CM: blocks occlusion culled", blocks_occlusion_culled); if(blocks_in_range != 0) g_profiler->avg("CM: blocks in range without mesh (frac)", (float)blocks_in_range_without_mesh/blocks_in_range); g_profiler->avg("CM: blocks drawn", blocks_drawn); g_profiler->avg("CM: wanted max blocks", m_control.wanted_max_blocks); }
/* sender_peer_id given to this shall be quaranteed to be a valid peer */ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id) { DSTACK(__FUNCTION_NAME); // Ignore packets that don't even fit a command if(datasize < 2) { m_packetcounter.add(60000); return; } ToClientCommand command = (ToClientCommand)readU16(&data[0]); //infostream<<"Client: received command="<<command<<std::endl; m_packetcounter.add((u16)command); /* If this check is removed, be sure to change the queue system to know the ids */ if(sender_peer_id != PEER_ID_SERVER) { infostream<<"Client::ProcessData(): Discarding data not " "coming from server: peer_id="<<sender_peer_id <<std::endl; return; } u8 ser_version = m_server_ser_ver; //infostream<<"Client received command="<<(int)command<<std::endl; if(command == TOCLIENT_INIT) { if(datasize < 3) return; u8 deployed = data[2]; infostream<<"Client: TOCLIENT_INIT received with " "deployed="<<((int)deployed&0xff)<<std::endl; if(deployed < SER_FMT_VER_LOWEST || deployed > SER_FMT_VER_HIGHEST) { infostream<<"Client: TOCLIENT_INIT: Server sent " <<"unsupported ser_fmt_ver"<<std::endl; return; } m_server_ser_ver = deployed; // Get player position v3s16 playerpos_s16(0, BS*2+BS*20, 0); if(datasize >= 2+1+6) playerpos_s16 = readV3S16(&data[2+1]); v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0); { //envlock //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out // Set player position Player *player = m_env.getLocalPlayer(); assert(player != NULL); player->setPosition(playerpos_f); } if(datasize >= 2+1+6+8) { // Get map seed m_map_seed = readU64(&data[2+1+6]); infostream<<"Client: received map seed: "<<m_map_seed<<std::endl; } // Reply to server u32 replysize = 2; SharedBuffer<u8> reply(replysize); writeU16(&reply[0], TOSERVER_INIT2); // Send as reliable m_con.Send(PEER_ID_SERVER, 1, reply, true); return; } if(command == TOCLIENT_ACCESS_DENIED) { // The server didn't like our password. Note, this needs // to be processed even if the serialisation format has // not been agreed yet, the same as TOCLIENT_INIT. m_access_denied = true; m_access_denied_reason = L"Unknown"; if(datasize >= 4) { std::string datastring((char*)&data[2], datasize-2); std::istringstream is(datastring, std::ios_base::binary); m_access_denied_reason = deSerializeWideString(is); } return; } if(ser_version == SER_FMT_VER_INVALID) { infostream<<"Client: Server serialization" " format invalid or not initialized." " Skipping incoming command="<<command<<std::endl; return; } // Just here to avoid putting the two if's together when // making some copypasta {} if(command == TOCLIENT_REMOVENODE) { if(datasize < 8) return; v3s16 p; p.X = readS16(&data[2]); p.Y = readS16(&data[4]); p.Z = readS16(&data[6]); //TimeTaker t1("TOCLIENT_REMOVENODE"); // This will clear the cracking animation after digging ((ClientMap&)m_env.getMap()).clearTempMod(p); removeNode(p); } else if(command == TOCLIENT_ADDNODE) { if(datasize < 8 + MapNode::serializedLength(ser_version)) return; v3s16 p; p.X = readS16(&data[2]); p.Y = readS16(&data[4]); p.Z = readS16(&data[6]); //TimeTaker t1("TOCLIENT_ADDNODE"); MapNode n; n.deSerialize(&data[8], ser_version); addNode(p, n); } else if(command == TOCLIENT_BLOCKDATA) { // Ignore too small packet if(datasize < 8) return; v3s16 p; p.X = readS16(&data[2]); p.Y = readS16(&data[4]); p.Z = readS16(&data[6]); /*infostream<<"Client: Thread: BLOCKDATA for (" <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/ /*infostream<<"Client: Thread: BLOCKDATA for (" <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/ std::string datastring((char*)&data[8], datasize-8); std::istringstream istr(datastring, std::ios_base::binary); MapSector *sector; MapBlock *block; v2s16 p2d(p.X, p.Z); sector = m_env.getMap().emergeSector(p2d); assert(sector->getPos() == p2d); //TimeTaker timer("MapBlock deSerialize"); // 0ms block = sector->getBlockNoCreateNoEx(p.Y); if(block) { /* Update an existing block */ //infostream<<"Updating"<<std::endl; block->deSerialize(istr, ser_version); } else { /* Create a new block */ //infostream<<"Creating new"<<std::endl; block = new MapBlock(&m_env.getMap(), p); block->deSerialize(istr, ser_version); sector->insertBlock(block); //DEBUG /*NodeMod mod; mod.type = NODEMOD_CHANGECONTENT; mod.param = CONTENT_MESE; block->setTempMod(v3s16(8,10,8), mod); block->setTempMod(v3s16(8,9,8), mod); block->setTempMod(v3s16(8,8,8), mod); block->setTempMod(v3s16(8,7,8), mod); block->setTempMod(v3s16(8,6,8), mod);*/ } #if 0 /* Acknowledge block */ /* [0] u16 command [2] u8 count [3] v3s16 pos_0 [3+6] v3s16 pos_1 ... */ u32 replysize = 2+1+6; SharedBuffer<u8> reply(replysize); writeU16(&reply[0], TOSERVER_GOTBLOCKS); reply[2] = 1; writeV3S16(&reply[3], p); // Send as reliable m_con.Send(PEER_ID_SERVER, 1, reply, true); #endif /* Update Mesh of this block and blocks at x-, y- and z-. Environment should not be locked as it interlocks with the main thread, from which is will want to retrieve textures. */ //m_env.getClientMap().updateMeshes(block->getPos(), getDayNightRatio()); /* Add it to mesh update queue and set it to be acknowledged after update. */ //infostream<<"Adding mesh update task for received block"<<std::endl; addUpdateMeshTaskWithEdge(p, true); } else if(command == TOCLIENT_PLAYERPOS) { infostream<<"Received deprecated TOCLIENT_PLAYERPOS" <<std::endl; /*u16 our_peer_id; { //JMutexAutoLock lock(m_con_mutex); //bulk comment-out our_peer_id = m_con.GetPeerID(); } // Cancel if we don't have a peer id if(our_peer_id == PEER_ID_INEXISTENT){ infostream<<"TOCLIENT_PLAYERPOS cancelled: " "we have no peer id" <<std::endl; return; }*/ { //envlock //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out u32 player_size = 2+12+12+4+4; u32 player_count = (datasize-2) / player_size; u32 start = 2; for(u32 i=0; i<player_count; i++) { u16 peer_id = readU16(&data[start]); Player *player = m_env.getPlayer(peer_id); // Skip if player doesn't exist if(player == NULL) { start += player_size; continue; } // Skip if player is local player if(player->isLocal()) { start += player_size; continue; } v3s32 ps = readV3S32(&data[start+2]); v3s32 ss = readV3S32(&data[start+2+12]); s32 pitch_i = readS32(&data[start+2+12+12]); s32 yaw_i = readS32(&data[start+2+12+12+4]); /*infostream<<"Client: got " <<"pitch_i="<<pitch_i <<" yaw_i="<<yaw_i<<std::endl;*/ f32 pitch = (f32)pitch_i / 100.0; f32 yaw = (f32)yaw_i / 100.0; v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.); v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.); player->setPosition(position); player->setSpeed(speed); player->setPitch(pitch); player->setYaw(yaw); /*infostream<<"Client: player "<<peer_id <<" pitch="<<pitch <<" yaw="<<yaw<<std::endl;*/ start += player_size; } } //envlock } else if(command == TOCLIENT_PLAYERINFO) { u16 our_peer_id; { //JMutexAutoLock lock(m_con_mutex); //bulk comment-out our_peer_id = m_con.GetPeerID(); } // Cancel if we don't have a peer id if(our_peer_id == PEER_ID_INEXISTENT){ infostream<<"TOCLIENT_PLAYERINFO cancelled: " "we have no peer id" <<std::endl; return; } //infostream<<"Client: Server reports players:"<<std::endl; { //envlock //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out u32 item_size = 2+PLAYERNAME_SIZE; u32 player_count = (datasize-2) / item_size; u32 start = 2; // peer_ids core::list<u16> players_alive; for(u32 i=0; i<player_count; i++) { // Make sure the name ends in '\0' data[start+2+20-1] = 0; u16 peer_id = readU16(&data[start]); players_alive.push_back(peer_id); /*infostream<<"peer_id="<<peer_id <<" name="<<((char*)&data[start+2])<<std::endl;*/ // Don't update the info of the local player if(peer_id == our_peer_id) { start += item_size; continue; } Player *player = m_env.getPlayer(peer_id); // Create a player if it doesn't exist if(player == NULL) { player = new RemotePlayer( m_device->getSceneManager()->getRootSceneNode(), m_device, -1); player->peer_id = peer_id; m_env.addPlayer(player); infostream<<"Client: Adding new player " <<peer_id<<std::endl; } player->updateName((char*)&data[start+2]); start += item_size; } /* Remove those players from the environment that weren't listed by the server. */ //infostream<<"Removing dead players"<<std::endl; core::list<Player*> players = m_env.getPlayers(); core::list<Player*>::Iterator ip; for(ip=players.begin(); ip!=players.end(); ip++) { // Ingore local player if((*ip)->isLocal()) continue; // Warn about a special case if((*ip)->peer_id == 0) { infostream<<"Client: Removing " "dead player with id=0"<<std::endl; } bool is_alive = false; core::list<u16>::Iterator i; for(i=players_alive.begin(); i!=players_alive.end(); i++) { if((*ip)->peer_id == *i) { is_alive = true; break; } } /*infostream<<"peer_id="<<((*ip)->peer_id) <<" is_alive="<<is_alive<<std::endl;*/ if(is_alive) continue; infostream<<"Removing dead player "<<(*ip)->peer_id <<std::endl; m_env.removePlayer((*ip)->peer_id); } } //envlock } else if(command == TOCLIENT_SECTORMETA) { infostream<<"Client received DEPRECATED TOCLIENT_SECTORMETA"<<std::endl; #if 0 /* [0] u16 command [2] u8 sector count [3...] v2s16 pos + sector metadata */ if(datasize < 3) return; //infostream<<"Client received TOCLIENT_SECTORMETA"<<std::endl; { //envlock //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out std::string datastring((char*)&data[2], datasize-2); std::istringstream is(datastring, std::ios_base::binary); u8 buf[4]; is.read((char*)buf, 1); u16 sector_count = readU8(buf); //infostream<<"sector_count="<<sector_count<<std::endl; for(u16 i=0; i<sector_count; i++) { // Read position is.read((char*)buf, 4); v2s16 pos = readV2S16(buf); /*infostream<<"Client: deserializing sector at " <<"("<<pos.X<<","<<pos.Y<<")"<<std::endl;*/ // Create sector assert(m_env.getMap().mapType() == MAPTYPE_CLIENT); ((ClientMap&)m_env.getMap()).deSerializeSector(pos, is); } } //envlock #endif } else if(command == TOCLIENT_INVENTORY) { if(datasize < 3) return; //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device); { //envlock //TimeTaker t2("mutex locking", m_device); //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out //t2.stop(); //TimeTaker t3("istringstream init", m_device); std::string datastring((char*)&data[2], datasize-2); std::istringstream is(datastring, std::ios_base::binary); //t3.stop(); //m_env.printPlayers(infostream); //TimeTaker t4("player get", m_device); Player *player = m_env.getLocalPlayer(); assert(player != NULL); //t4.stop(); //TimeTaker t1("inventory.deSerialize()", m_device); player->inventory.deSerialize(is); //t1.stop(); m_inventory_updated = true; //infostream<<"Client got player inventory:"<<std::endl; //player->inventory.print(infostream); } } //DEBUG else if(command == TOCLIENT_OBJECTDATA) { // Strip command word and create a stringstream std::string datastring((char*)&data[2], datasize-2); std::istringstream is(datastring, std::ios_base::binary); u8 buf[12]; /* Read players */ is.read((char*)buf, 2); u16 playercount = readU16(buf); for(u16 i=0; i<playercount; i++) { is.read((char*)buf, 2); u16 peer_id = readU16(buf); is.read((char*)buf, 12); v3s32 p_i = readV3S32(buf); is.read((char*)buf, 12); v3s32 s_i = readV3S32(buf); is.read((char*)buf, 4); s32 pitch_i = readS32(buf); is.read((char*)buf, 4); s32 yaw_i = readS32(buf); Player *player = m_env.getPlayer(peer_id); // Skip if player doesn't exist if(player == NULL) { continue; } // Skip if player is local player if(player->isLocal()) { continue; } f32 pitch = (f32)pitch_i / 100.0; f32 yaw = (f32)yaw_i / 100.0; v3f position((f32)p_i.X/100., (f32)p_i.Y/100., (f32)p_i.Z/100.); v3f speed((f32)s_i.X/100., (f32)s_i.Y/100., (f32)s_i.Z/100.); player->setPosition(position); player->setSpeed(speed); player->setPitch(pitch); player->setYaw(yaw); } /* Read block objects NOTE: Deprecated stuff */ // Read active block count u16 blockcount = readU16(is); if(blockcount != 0){ infostream<<"TOCLIENT_OBJECTDATA: blockcount != 0 " "not supported"<<std::endl; return; } } else if(command == TOCLIENT_TIME_OF_DAY) { if(datasize < 4) return; u16 time_of_day = readU16(&data[2]); time_of_day = time_of_day % 24000; //infostream<<"Client: time_of_day="<<time_of_day<<std::endl; /* time_of_day: 0 = midnight 12000 = midday */ { m_env.setTimeOfDay(time_of_day); u32 dr = m_env.getDayNightRatio(); infostream<<"Client: time_of_day="<<time_of_day <<", dr="<<dr <<std::endl; } } else if(command == TOCLIENT_CHAT_MESSAGE) { /* u16 command u16 length wstring message */ u8 buf[6]; std::string datastring((char*)&data[2], datasize-2); std::istringstream is(datastring, std::ios_base::binary); // Read stuff is.read((char*)buf, 2); u16 len = readU16(buf); std::wstring message; for(u16 i=0; i<len; i++) { is.read((char*)buf, 2); message += (wchar_t)readU16(buf); } /*infostream<<"Client received chat message: " <<wide_to_narrow(message)<<std::endl;*/ m_chat_queue.push_back(message); } else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD) { //if(g_settings->getBool("enable_experimental")) { /* u16 command u16 count of removed objects for all removed objects { u16 id } u16 count of added objects for all added objects { u16 id u8 type u32 initialization data length string initialization data } */ char buf[6]; // Get all data except the command number std::string datastring((char*)&data[2], datasize-2); // Throw them in an istringstream std::istringstream is(datastring, std::ios_base::binary); // Read stuff // Read removed objects is.read(buf, 2); u16 removed_count = readU16((u8*)buf); for(u16 i=0; i<removed_count; i++) { is.read(buf, 2); u16 id = readU16((u8*)buf); // Remove it { //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out m_env.removeActiveObject(id); } } // Read added objects is.read(buf, 2); u16 added_count = readU16((u8*)buf); for(u16 i=0; i<added_count; i++) { is.read(buf, 2); u16 id = readU16((u8*)buf); is.read(buf, 1); u8 type = readU8((u8*)buf); std::string data = deSerializeLongString(is); // Add it { //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out m_env.addActiveObject(id, type, data); } } } } else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES) { //if(g_settings->getBool("enable_experimental")) { /* u16 command for all objects { u16 id u16 message length string message } */ char buf[6]; // Get all data except the command number std::string datastring((char*)&data[2], datasize-2); // Throw them in an istringstream std::istringstream is(datastring, std::ios_base::binary); while(is.eof() == false) { // Read stuff is.read(buf, 2); u16 id = readU16((u8*)buf); if(is.eof()) break; is.read(buf, 2); u16 message_size = readU16((u8*)buf); std::string message; message.reserve(message_size); for(u16 i=0; i<message_size; i++) { is.read(buf, 1); message.append(buf, 1); } // Pass on to the environment { //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out m_env.processActiveObjectMessage(id, message); } } } } else if(command == TOCLIENT_HP) { std::string datastring((char*)&data[2], datasize-2); std::istringstream is(datastring, std::ios_base::binary); Player *player = m_env.getLocalPlayer(); assert(player != NULL); u8 hp = readU8(is); player->hp = hp; } else if(command == TOCLIENT_MOVE_PLAYER) { std::string datastring((char*)&data[2], datasize-2); std::istringstream is(datastring, std::ios_base::binary); Player *player = m_env.getLocalPlayer(); assert(player != NULL); v3f pos = readV3F1000(is); f32 pitch = readF1000(is); f32 yaw = readF1000(is); player->setPosition(pos); /*player->setPitch(pitch); player->setYaw(yaw);*/ infostream<<"Client got TOCLIENT_MOVE_PLAYER" <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")" <<" pitch="<<pitch <<" yaw="<<yaw <<std::endl; /* Add to ClientEvent queue. This has to be sent to the main program because otherwise it would just force the pitch and yaw values to whatever the camera points to. */ ClientEvent event; event.type = CE_PLAYER_FORCE_MOVE; event.player_force_move.pitch = pitch; event.player_force_move.yaw = yaw; m_client_event_queue.push_back(event); // Ignore damage for a few seconds, so that the player doesn't // get damage from falling on ground m_ignore_damage_timer = 3.0; } else if(command == TOCLIENT_PLAYERITEM) { std::string datastring((char*)&data[2], datasize-2); std::istringstream is(datastring, std::ios_base::binary); u16 count = readU16(is); for (u16 i = 0; i < count; ++i) { u16 peer_id = readU16(is); Player *player = m_env.getPlayer(peer_id); if (player == NULL) { infostream<<"Client: ignoring player item " << deSerializeString(is) << " for non-existing peer id " << peer_id << std::endl; continue; } else if (player->isLocal()) { infostream<<"Client: ignoring player item " << deSerializeString(is) << " for local player" << std::endl; continue; } else { InventoryList *inv = player->inventory.getList("main"); std::string itemstring(deSerializeString(is)); if (itemstring.empty()) { inv->deleteItem(0); infostream <<"Client: empty player item for peer " << peer_id << std::endl; } else { std::istringstream iss(itemstring); delete inv->changeItem(0, InventoryItem::deSerialize(iss)); infostream<<"Client: player item for peer " << peer_id << ": "; player->getWieldItem()->serialize(infostream); infostream<<std::endl; } } } } else if(command == TOCLIENT_DEATHSCREEN) { std::string datastring((char*)&data[2], datasize-2); std::istringstream is(datastring, std::ios_base::binary); bool set_camera_point_target = readU8(is); v3f camera_point_target = readV3F1000(is); ClientEvent event; event.type = CE_DEATHSCREEN; event.deathscreen.set_camera_point_target = set_camera_point_target; event.deathscreen.camera_point_target_x = camera_point_target.X; event.deathscreen.camera_point_target_y = camera_point_target.Y; event.deathscreen.camera_point_target_z = camera_point_target.Z; m_client_event_queue.push_back(event); } else { infostream<<"Client: Ignoring unknown command " <<command<<std::endl; } }